如何自定义 favicon
<link rel="shortcut icon" type="image/png" href="/favicon.png"/>
html 中的空格
multiple spaces in html is displayed as one, to show multiple space
- use
<pre>
around the word - use
<link rel="shortcut icon" type="image/png" href="/favicon.png"/>
multiple spaces in html is displayed as one, to show multiple space
<pre>
around the word
之前在网页里小范围的用了 vue, 感觉用起来非常爽,现在打算做自己的笔记应用。这次打算做成 SPA 的形式,前端全部用 vue 来写。需要 node 的一些东西,记录下学习过程。迈向全栈,哈哈 ^_^
首先,全局安装 vue:
npm install --global @vue/cli
只有在创建时提供的 data 属性才是响应式的,在创建之后在添加新元素就不管用了。
当然还可以使用 app.$watch 方法显式创建一些监听器
// $watch 是一个实例方法
vm.$watch("a", function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
methods 定义了一些可以调用的方法,他的值也可以用来插值。但是最好使用 computed,因为 computed 是有缓存的。
computed 属性也可以设置 setter,所以实际上,computed 属性相当于对现有属性的一种映射和变化。
{{ }}
。只能使用表达式,而不能使用语句。v-bind 类似的指令还有 v-once 和 v-html
v-model 实际上等价于
<input v-model="searchText">
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
要想在自定义输入组件中支持 v-model 的话,就需要使用 v-bind 和 v-on 两个方法了,而不能直接使用 v-model。
这两个就和所有模板系统中的 if 和 for 一样。vue 中提供的额外方便之处是,可以使用 template 标签,这样就不会多一个标签了
vue 是懒渲染的,因此会尽可能地复用组件,可以使用 key 来区分
v-if 是真正的条件式渲染,v-show 则只是在切换 display 属性
vue 没有代理数组的赋值方法,所以需要使用 app.$set 方法
如果只是指定一个事件处理函数的话,那么参数就是 event。如果自己指定了参数的话,可以使用 $event 来代表 event
还可以使用 .prevent 和 .stop 等修饰符
vue 中最终要的概念就是组件了。使用组件来模块式得构建应用。需要通过 props 属性来定义组件中的属性
<div id="app-7">
<ol>
<!--
现在我们为每个 todo-item 提供 todo 对象
todo 对象是变量,即其内容可以是动态的。
我们也需要为每个组件提供一个“key”,稍后再
作详细解释。
-->
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id">
</todo-item>
</ol>
</div>
Vue.component("todo-item", {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义特性。
// 这个 prop 名为 todo。
props: ["todo"],
template: "<li>{{ todo.text }}</li>"
})
props 是一个数组,用来声明组建的属性。然后通过属性来传递。
组件还可以通过 $emit 来发送事件,这些事件可以被所有的组件监听到,就像普通的 DOM 事件一样。
在固定的周期,vue 会调用的一些函数 created, mounted 等。需要注意的是,不要使用胖箭头函数。
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log("a is: " + this.a)
}
})
// => "a is: 1"
因为我们会直接通过*.vue
文件来编写 vue 的组件,因此需要使用 webpack 打包编译。另外我们需要使用官方的 vue-router 来
vue init webpack notelet
这条命令基于 webpack 这个模板创建了 notelet 这个应用,也就是我们的笔记应用。
打开 src/router/index.js
可以看到 vue 创建的 router 的代码,其中@
是src
目录的缩写。
import Vue from "vue"
import Router from "vue-router"
import Hello from "@/components/Hello"
Vue.use(Router)
export default new Router({
routes: [
{
¦ path: "/",
¦ name: "Hello",
¦ component: Hello
}
]
})
然后打开 src/main.js
, 可以看到在里面使用了 App
来作为我们的跟组件
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue"
import App from "./App"
import router from "./router"
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: "#app",
router,
template: "<App/>",
components: { App }
})
执行 npm run dev
, 然后打开 http://localhost:8080/ 就可以看到我们的 vue 应用了。注意,在写这篇文章的时候 node 8.x 下似乎有 bug, 导致 app.js 加载不出来,安装 6.x 就好了。该死的 node.
打开 App.vue
, 也就是我们的根组件,可以看到下面的内容
<template>
<div id="app">
¦ <router-view></router-view>
</div>
</template>
<script>
export default {
name: "app"
}
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
注意其中的 router-view 便签,意思就是路由的内容都在 router-view 中显示。
接下来我们添加一个”关于”页面。打开src/router/index.js
, 改成下面这样:
export default new Router({
routes: [
¦ {
¦ ¦ path: "/",
¦ ¦ name: "Hello",
¦ ¦ component: Hello
¦ },
¦ {
¦ ¦ path: "/about",
¦ ¦ name: "About",
¦ ¦ component: About
¦ }
]
})
然后添加 src/components/About.vue
文件
<template>
<div class="hello">
¦ <h1>About Notelet</h1>
¦ <p>This is a simple note app</p>
</div>
</template>
<script>
export default {
name: "About",
data () {
¦ return {
¦ ¦ msg: "Hello Vue"
¦ }
}
}
</script>
然后更改 App.vue
文件
<template>
<div id="app">
¦ <router-link :to="{name: "Hello"}">Home</router-link>
¦ <router-link to="/about">About</router-link>
¦ <router-view></router-view>
</div>
</template>
注意,我们使用 about 指向了 About 这个组件,而使用 hello 指向了 Hello 这个组件,注意其中还动态传递了参数。
[1] https://scotch.io/tutorials/getting-started-with-vue-router
写爬虫的话做到最后基本上最终没法自动化的就是指定要抽取的元素的 xpath 了,要定向爬一个网站的内容基本上都会归结到去找下一页和数据元素的 xpath. 如果能把 xpath 的生成交给不会写程序的运营同学来做的话,能够极大地解放程序员的生产力。
毕竟 xpath 也算是一个 DSL, 对于不会编程的同学还是有一定难度的。SQL 写得熟练的 PM 多得是,想找一个会写 xpath 的运营同学则是很困难,毕竟术业有专攻,运营需要面对的问题和我们程序猿还是有很大不同。多年的经验,感觉能教会他们 yaml 已经是极限了…
那么能不能有一个图形化的工具来生成 xpath 呢?答案显然是有的,chrome 浏览器就内置了生成 xpath 的工具,如下图所示:
这幅图生成的 xpath 是://*[@id="fc_B_pic"]/ul[1]/li[1]/a[1]
然而 chrome 的 xpath 生成却有几个缺点:
未完待续
HTML Document consists of different node, element is one type of node.
NodeList is a collection of node, HTMLCollection is a collection of element.
HTMLCollection 和 NodeList 都是动态的,会随着 DOM 的变化而变化
Array 是静态的数据结构
chrome set focus to the omni box when you create a new tab, although there is an api to replace the new tab page. you can’t steal the focus from the omni box in the new tab page simply. there are two work-arounds.
https://stackoverflow.com/questions/42178723/chrome-extension-creating-new-tab-and-taking-focus-to-page
https://stackoverflow.com/questions/17598778/how-to-steal-focus-from-the-omnibox-in-a-chrome-extension-on-the-new-tab-page
https://stackoverflow.com/questions/22193369/run-chrome-extensions-using-command-prompt
chrome/firefox 插件源码的位置:
~/Library/Application Support/Google/Chrome/Default/Extensions/<EXTENSION_ID>/
~/Library/Application Support/Firefox/Profiles/PROFILE_ID/extensions/EXTENSION_ID/
fetch 是近年来浏览器实现的一个用于取代 xhr 的 API,相比于 xhr 来说更加简单易用安全且强大。主要区别有:
如果要在 node 中使用的话,需要安装 isomophic-unfetch:
yarn add isomorphic-unfetch
import fetch from 'isomorphic-unfetch'
const response = await fetch("http://example.com/movies.json");
const myJson = await response.json(); // text() 返回纯文本
console.log(JSON.stringify(myJson));
特别注意的是,在 Flask 等后端框架中,如果不添加 application/json
的 header, 默认不会解析 json, 太坑了。默认情况下 fetch 是会携带同一个域名下的 Cookie 的
try {
// Default options are marked with *
const response = await fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, *cors, same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
headers: {
"Content-Type": "application/json"
// "Content-Type": "application/x-www-form-urlencoded",
},
redirect: "follow", // manual, *follow, error
referrer: "no-referrer", // no-referrer, *client
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
console.log(await response.json());
} catch (error) {
console.error(error);
}
var url = new URL('https://sl.se')
var params = {lat:35.696233, long:139.570431} // or:
var params = [['lat', '35.696233'], ['long', '139.570431']]
url.search = new URLSearchParams(params).toString();
fetch(url)
在 Node 中可能需要:import {URL, URLSearchParams} from 'url'
fetch 的 credentials 有三个选项:
omit
不携带任何 cookiesinclude
携带所有 cookiessame-origin
只有向当前网站的同源域名发送请求时才携带 cookies其中 same-origin
是默认选项。
const headers = new Headers();
headers.append("Content-Type", "text/plain");
headers.append("Content-Length", content.length.toString());
headers.append("X-Custom-Header", "ProcessThisImmediately");
// 另一种方法是直接使用字典
const headers = new Headers({
"Content-Type": "text/plain",
})
const init = {
method: "POST",
headers: headers,
mode: "cors",
cache: "default",
}
const request = new Request("flowers.jpg", init);
const rsp = await fetch(request);
chrome.runtime.getBackgroundPage(function(window) {}) // retrive the background page's window object
chrome.runtime.getURL(path) // get the absolute url for given file in extension package
permissions: [
"tabs",
]
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {}); // tabs[0] would be the current tab
chrome.tabs.create({url: URL}, function(tab) {})
chrome.tabs.remove(tabId or [tabId], function() {})
there are 3 storage area for chrome, sync
, local
, managed
areas. the sync
area will be synced with the cloud. managed area is read-only.
all your extension scripts share the same storage, including content scripts, they don’t belong to their domain’s localStorage.
chrome.storage.local.get('key', function(data) {});
chrome.storage.local.get(["KEY1", "KEY2"], function(data) {});
chrome.storage.local.set(data, function() {}); // data is key-value pair to store
chrome.storage.local.remove('key', function() {});
chroem.storage.local.remove(["KEY1", "KEY2"], function() {});
chrome.storage.local.clear(function() {});