响应式原理
1 | var data = { name:"poetries "} |
router-link用法
router-link默认渲染为
href 1
2
3
4
5
6```html
<router-link tag="a" :to="'/city/' + guessCityid" class="guess_city">
<span>{{guessCity}}</span>
</router-link>
渲染为
<a data-v-3ea254f4="" href="#/city/2" class="guess_city"><span data-v-3ea254f4="">杭州</span></a>router-link tag属性可设置为li,span等等,渲染为li,span
:to内有path, query参数 path负责切换路由,query为路由参数
1 | <router-link :to="{path: '/food', query: {geohash, title: foodItem.title, restaurant_category_id: getCategoryId(foodItem.link)}}" v-for="foodItem in item" :key="foodItem.id" class="link_to_food"> |
Vue项目中props,data()中数据的区别
- props用于存放模板,其他函数运行时需要的变量;接住父模板传递过来的参数;也可以是模板运行时的函数名称。
- data(){ }存放响应式的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64export default {
data(){
return {
offset: 0, // 批次加载店铺列表,每次加载20个 limit = 20
shopListArr:[], // 店铺列表数据
preventRepeatReuqest: false, //到达底部加载数据,防止重复加载
showBackStatus: false, //显示返回顶部按钮
showLoading: true, //显示加载动画
touchend: false, //没有更多数据
imgBaseUrl,
}
},
mounted(){
this.initData();
},
components: {
loading,
ratingStar,
},
props: ['restaurantCategoryId', 'restaurantCategoryIds', 'sortByType', 'deliveryMode', 'supportIds', 'confirmSelect', 'geohash'],
mixins: [loadMore, getImgPath],
computed: {
...mapState([
'latitude','longitude'
]),
},
updated(){
// console.log(this.supportIds, this.sortByType)
},
methods: {
async initData(){
//获取数据
let res = await shopList(this.latitude, this.longitude, this.offset, this.restaurantCategoryId);
....
},
//到达底部加载更多数据
async loaderMore(){
...
},
//返回顶部
backTop(){
...
},
//监听父级传来的数据发生变化时,触发此函数重新根据属性值获取数据
async listenPropChange(){
...
},
},
watch: {
//监听父级传来的restaurantCategoryIds,当值发生变化的时候重新获取餐馆数据,作用于排序和筛选
restaurantCategoryIds: function (value){
this.listenPropChange();
},
//监听父级传来的排序方式
sortByType: function (value){
this.listenPropChange();
},
//监听父级的确认按钮是否被点击,并且返回一个自定义事件通知父级,已经接收到数据,此时父级才可以清除已选状态
confirmSelect: function (value){
this.listenPropChange();
}
}
}
Vue项目中模板传递数据,条件判断写法
Head.vue 抽象基础模板
- 对外暴露的参数写法均为驼峰式 goBack headTitle signinUp
- slot name = ‘logo/search/edit’ 占位卡槽
1 | <template> |
Home.vue
* 将head.vue作为基础模板引入
- 传递参数用 连接字符命名 signin-up对应参数signinUp
slot 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26```html
<template>
<div>
<head-top signin-up='home'>
<span slot='logo' class="head_logo" @click="reload"><!--ele.me-->首页</span>
<!--传入公共模板head.vue的占位卡槽-->
</head-top>
...
</div>
</template>
<script>
import headTop from 'head.vue'
export default {
data(){ }
mounted(){ }
components:{
headTop //模板中可以使用head-top载入
},
methods:{
//点击图标刷新页面
reload(){
window.location.reload();
}
}
}
</script>几种数据展示方式
- head-top 导入的组件
- :head-title -> v-bind:head-title go-back 子模板接口
- cityname placelist nextpage 当前模板变量小写名称,函数名称
- slot=”changecity”卡槽名称
- 不在””内,对外暴露的模板参数
- class=”title_head ellipsis” 模板类名
- v-for=”(item, index) in placelist” each数据操作
- @click 模板绑定方法
- :key=”index” ->v-bind:key=”index” 性能优化方法
- 不在””内,当前对象的字面量值
- :class=”{choose_type:sortBy == ‘food’}” class判断逻辑
一小段代码就能弄出11条指令的不同用法,Vue其实也不方便。
1 |
|
webpack中利用require.ensure()实现按需加载
- 空数组作为参数
1 | require.ensure([], function(require){ |
以上代码保证了拆分点被创建,而且 a.js 被 webpack 分开打包。
- 依赖作为参数
1
2
3require.ensure(['./a.js'], function(require) {
require('./b.js');
});
上面代码, a.js 和 b.js 都被打包到一起,而且从主文件束中拆分出来。但只有 b.js 的内容被执行。a.js 的内容仅仅是可被使用,但并没有被输出。
想去执行 a.js,我们需要异步地引用它,如 require(‘./a.js’),让它的 JavaScritp 被执行。
- 单独打包成自己写的名字配置
需要配置chunkFilename,和publicPath。publicPath是按需加载单独打包出来的chunk是以publicPath会基准来存放的,chunkFilename:[name].js这样才会最终生成正确的路径和名字1
2
3
4
5
6
7
8module.exports={
entry:'./js/entry.js',
output:{
path:path.resolve(__dirname,"./dist"),
filename:'js/a.bundle.js',
publicPath:"./",
chunkFilename:'js/[name].js'
}
所以router/index.js 修改为懒加载组件:1
2
3
4const Province = r => require.ensure([], () => r(require('@/components/Province.vue')), 'chunkname1')
const Segment = r => require.ensure([], () => r(require('@/components/Segment.vue')), 'chunkname2')
const Loading = r => require.ensure([], () => r(require('@/components/Loading.vue')), 'chunkname3')
const User = r => require.ensure([], () => r(require('@/components/User.vue')), 'chunkname3')
根据 chunkame的不同, 上面的四个组件, 将会被分成3个块打包,最终打包之后与组件相关的js文件会分为3个 (除了app.js,manifest.js, vendor.js)
main.js
import routes from ‘./router/router’
console.log(routes)
/router/router.js
import App from ‘../App’
console.log(App);
Vuex 用Object.assign 修改数据
1 | //修改用户名 |
VuePress文档系统搭建
深度观测如何实现?
1 | function _traverse (val: any, seen: SimpleSet) { |
最新版的nextTick不是走宏任务,微任务的逻辑,是根据终端的特性判断。
1 | if (typeof Promise !== 'undefined' && isNative(Promise)) { |
if (typeof Promise !== ‘undefined’ && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
// In problematic UIWebViews, Promise.then doesn’t completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn’t being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// “force” the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## 什么是异步更新队列?
queueWatcher 函数的作用就是我们前面讲到过的,它将观察者放到一个队列中等待所有突变完成之后统一执行更新。
## !this.depIds.has(id) { dep.addSub(this) } 在依赖收集逻辑里,这条语句为何可以控制重复收集?
* 1、newDepIds 属性用来在一次求值中避免收集重复的观察者
* 2、每次求值并收集观察者完成之后会清空 newDepIds 和 newDeps 这两个属性的值,并且在被清空之前把值分别赋给了 depIds 属性和 deps 属性
* 3、depIds 属性用来避免重复求值时收集重复的观察者
通过以上三点内容我们可以总结出一个结论,即 newDepIds 和 newDeps 这两个属性的值所存储的总是当次求值所收集到的 Dep 实例对象,而 depIds 和 deps 这两个属性的值所存储的总是上一次求值过程中所收集到的 Dep 实例对象。
```js
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
答:watch 在get函数中 finally 语句块内调用了观察者对象的 cleanupDeps 方法,这个方法的作用在于 每次求值完毕后都会使用 depIds 属性和 deps 属性保存 newDepIds 属性和 newDeps 属性的值,然后再清空 newDepIds 属性和 newDeps 属性的值
1 | get () { |
vm._render 函数的作用是调用 vm.$options.render 函数并返回生成的虚拟节点(vnode)
vm._update 函数的作用是把 vm._render 函数生成的虚拟节点渲染成真正的DOM
问题?1虚拟节点vnode是什么?
2如何渲染虚拟节点
vm.$el是什么?
1 | const vm = new Vue({ |
理解 Vue中的Render渲染函数 ,Vue.createElement方法
Vue.set不能在根节点上添加属性。
1 | const data = { |
Vue.set是干什么用的?
Vue 是没有能力拦截到为一个对象(或数组)添加属性(或元素)的,
而 Vue.set 和 Vue.delete 就是为了解决这个问题而诞生的。同时为了方便使用1
2
3
4
5
6const ins = new Vue({
data: {
arr: [1, 2] }
})
ins.$data.arr[0] = 3 // 不能触发响应
ins.$set(ins.$data.arr, 0, 3) // 能够触发响应
##如何理解Vue里defineReactive这个函数?
defineReactive 这个函数包含 Object.defineProperty这个关键函数。在Vue源码里主要负责取data中的key,value。
在Object.defineProperty 外部new Dep实例。 为每个data的key 闭包绑定一个Dep,负责在get,set外部传入Dep
get 一个是返回正确的属性值,另一个是收集依赖。 Dep.target.depend()
set 函数也要完成两个重要的事情,第一正确地为属性设置新值,第二是能够触发相应的依赖。notify
Vue.set如何触发依赖收集?
$set 或 Vue.set 给数据对象添加新属性时触发,我们知道由于 js 语言的限制,在没有 Proxy 之前 Vue 没办法拦截到给对象添加属性的操作。
所以 Vue 才提供了 $set 和 Vue.set 等方法让我们有能力给对象添加新属性的同时触发依赖,那么触发依赖是怎么做到的呢?1
2
3
4Vue.set = function (obj, key, val) {
defineReactive(obj, key, val)
obj.__ob__.dep.notify()
}
notify 调用watch.run,确认data依赖,变更oldVal = newVal, 执行callback
vue2.0中的组件的继承与扩展
一、slot
1.默认插槽和匿名插槽
1 | <body> |
2.具名插槽1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31<div id="itany">
<my-hello>
<ul slot="s1">
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</ul>
<ol slot="s2">
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
</my-hello>
</div>
<template id="hello">
<div>
<slot name="s2"></slot>
<h3>welcome to xiamen</h3>
<slot name="s1"></slot>
</div>
</template>
<script>
var vm = new Vue({
el:'#itany',
components:{
'my-hello':{
template:'#hello'
}
}
})
</script>
二、mixins1
2
3一般有两种用途:
1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。
2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。
1 | <h1>Mixins</h1> |
三丶extends
1 extends用法
extends选项允许声明扩展另一个组件,而无需使用 Vue.extend。它和混入mixins非常的类似。只不过接收的参数是简单的选项对象或构造函数,所以extends只能单次扩展一个组件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<h1>Extends</h1>
<hr>
<div id="app">
num:{{ num }}
<p>
<button @click="add">add</button>
</p>
</div>
<script type="text/javascript">
var bbb = {
updated() {
console.log("我是被扩展出来的");
},
methods: {
add: function () { //跟原生的方法冲突,取原生的方法,这点跟混入一样
console.log('我是被扩展出来的add方法!');
this.num++;
}
}
};
var app = new Vue({
el: '#app',
data: {
num: 1
},
methods: {
add: function () {
console.log('我是原生add方法');
this.num++;
}
},
updated() {
console.log("我是扩展出来的");
},
extends: bbb //<font color="red">接收对象和函数</font>
})
</script>
四 extend
Vue.extend只是创建一个构造器,它是为了创建可复用的组件。其主要用来服务于Vue.component,用来生成组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div id="itany">
<hello></hello>
<my-world></my-world>
</div>
<script>
//1.使用Vue.extend()创建一个组件构造器
var MyComponent = Vue.extend({
template: '<h3>Hello World</h3>'
});
//2.使用Vue.component(标签名,组件构造器),根据组件构造器来创建组件
Vue.component('hello', MyComponent);
Vue.component('my-world', {
template: '<h1>你好,世界</h1>'
});
var vm = new Vue({ //这里的vm也是一个组件,称为根组件Root
el: '#itany',
data: {}
});
</script>
propsdata是干什么的?
propsdata 创建实例时传递 props。主要作用是方便测试。1
2
3
4
5
6
7
8
9var Comp = Vue.extend({
props: ['msg'],
template: '<div>{{ msg }}</div>'
})
var vm = new Comp({
propsData: {
msg: 'hello'
}
})
Vue源码里选项合并有两种方式
1 主动调用 外部调用场景new Vue(options) 的方式实例化一个 Vue 对象;另一种是我们上一节分析的组件过程中内部通过 new Vue(options) 实例化子组件。
都会执行实例的 _init(options) 方法,它首先会执行一个 merge options 的逻辑,相关的代码在 src/core/instance/init.js 中:
2 组件场景
Vue.extend父子传递, extends父子传递,mixins, new Vue等等,propsData父子传递,如何对父子选项进行合并处理的,也知道了它的作用
源代码看起来总之很麻烦
怎么看Vue源代码?
发现Vue源码里typescript的语法很适合看代码。有参数函数返回值,非常直观。
配合文档https://cn.vuejs.org/ http://hcysun.me/vue-design/一步一步看还是比较舒服的。
Vue生命周期图详解 https://segmentfault.com/a/1190000008570622
Vue2.0 $set() https://blog.csdn.net/panyang01/article/details/76665448
mounted () {
this.$set(this.student,”age”, 24) $set()方法,既可以新增属性,又可以触发视图更新。
}
如何实现一个简单的Vue?
- 每一个v-html, v-text等带有的表达式 内部都有一个Watch对象,在compile模板的过程中,递归fragment,绑定对应事件过程进行的。
- 编译过程为逐行编译,并非统一编译。 比如碰到一个 编译过程中直接填上data ,new一个watcher绑定到当前指令上,watcher内部再取一次
的值,触发一次this.get( ) Dep.target = this; 拿到name的值,这个过程中触发defineProperty get( )建立 dep和watch的关系。 - watch里的this.depIds ={ } 把dep 压入进去, dep里的 this.subs=[ ] 把watch放进去
- 一个watch里面只能有一个dep, 一个dep里面可以有多个watch (dep其实就是可动态更改的对象数目)
(watch是模板指令数目)对象在模板上可以对应多个指令进行模板修改。 - 当method通过addEventListener触发是,Observer监测到对象有变化,在set( )方法里调用Dep的notify方法,对保存在Dep里的Watch进行迭代,调用Watch里的update方法,update方法拿到新set的对象的值,对比老对象的值,调用各个指令节点绑定的方法,进行指令替换。
new MVVM 构造函数里。
- 解析指令 解析模板里的指令。
初始化视i图-> 逐行解析指令,把属性值填入到指令里。
订阅数据变化-> 把属性值填入指令里下面接着就绑定给指令节点绑定更新函数,并new一个Watch对象。绑定到当前指令上,watcher构建的时再取
指令的值,拿到name的值,触发一次this.get( )
添加订阅者 -> Dep.target = this; 这个过程中触发defineProperty get( )建立 dep和watch的关系。
watch里的this.depIds ={ } 把dep 压入进去, dep里的 this.subs=[ ] 把watch放进去
一个watch里面只能有一个dep, 一个dep里面可以有多个watch (dep其实就是可动态更改的对象数目)
(watch是模板指令数目)对象在模板上可以对应多个指令进行模板修改。 - 监听属性,Observer 给每个对象变量进行劫持。并初始化Dep对象,以便进行闭包调用
- 当method通过addEventListener触发是,Observer监测到对象有变化,在set( )方法里调用Dep的notify方法,对保存在Dep里的Watch进行迭代,调用Watch里的update方法,update方法拿到新set的对象的值,对比老对象的值,调用各个指令节点绑定的方法,进行指令替换。
SSR服务端渲染
Vue在项目中的使用
(下面全部搞懂了,Vue基本就会用了。)
Vue模板中所有的数据字段由数据库接管。
Vue数据流:
1
2
3
4
5{
单页面组件数据,data(){ this.data=..} 用来写入template。
props父亲传子组件数据,父子数据传递
Vuex全局组件共享数据源,记录APP共享的状态机
}Vue生命周期:
- Vue路由对象使用 VueRouter
- VueLoader(载入程序) js, scss sass,css,less typescript转换工具
- Vue服务端渲染(未知)
VueX 在项目里是怎么用的?
import {mapState, mapActions} from 'vuex'
export default {
data(){
return{
}
},
mounted(){
//获取用户信息
this.getUserInfo();
},
props: ['signinUp', 'headTitle', 'goBack'],
computed: {
...mapState([
'userInfo'
]),
//映射this.userInfo 为 store.state.userInfo
//this.userInfo = store.state.userInfo;
//this.userInfo没有在data里声明,template也可以直接拿来用。
},
methods: {
...mapActions([
'getUserInfo'
]),
//this.getUserInfo没有声明,可以直接拿来用。
},
}
webpackBootstrap
其他
- px rem的区别 https://www.cnblogs.com/huanghuali/p/6931080.html
- 在根元素中定义了一个基本字体大小为62.5%(也就是10px。设置这个值主要方便计算,如果没有
设置,将是以“16px”为基准 );对于只需要适配少部分手机设备,且分辨率对页面影响不大的,使用px即可
对于需要适配各种移动设备,使用rem,例如只需要适配iPhone和iPad等分辨率差别比较挺大的设备 v-bind 用来操作html原生属性 https://blog.csdn.net/gao_xu_520/article/details/76172406
Vue props用法小结 https://blog.csdn.net/q3254421/article/details/84068341 父组件用来给子组件传值,可以用到props
export 和 default export的区别 https://www.jianshu.com/p/edaf43e9384f
1 | <style lang="scss" scoped> |
2017-03-15
$ref 获取组件内的元素,
$parent:获取当前组件对象的父组件
$children:获取子组件
$root: 获取new Vew的实例
$el:组件对象的DOM元素
1 | VueRouter -> |
Vue.$nextTick 获取更新后的 DOM,在 DOM 更新循环结束之后执行延迟回调
2017-03-14
Vue对象生命周期
computed:凡是函数内部有this.相关属性,改变都会触发当前函数,监视多个
watch: 监视单个
Vue和原生,jQueryAPI替代
1 | //$选择器可以用ref替代 |
监听原生组件的事件,当获取到原生组件的值后把 值通过调用 $emit(‘input’ ,data) 方法去触发 input事件
v-bind 可以给任何赋值属性,是从value到页面的单向数据流 简写成 :title1
2
3
4
5
6
7<li v-for="item in items”>
{{ item.message }}
</li>
$.each(items,funciton(i,obj){
$(elem).parent().append("<li>...</li>")
})
v-style :style -> $.addClassRule
props用在定义自定义标签组件的属性上,v-bind用在定义原生标签属性上。1
2
3
4
5
6
7
8<span v-text="myText"></span>
<hr />
<span v-html="myHtml"></span>
<button v-if="isExit"></button>
<button v-if="num==1">测试Num1</button>
<button v-else-if="num==2">测试Num2</button>
<button v-show="isShow">v-show<button>
v-bind是 一个vue 指令,用于绑定 html 属性1
2
3
4
5
6
7
8
9
10
11
12
13
14<div id="app”>
<p v-bind:title="t1 + ' ' + t2">
html属性不能使用双大括号形式绑定,只能使用v-bind指令
</p>
可简写成
<p :title="t1+' '+t2"></p>
</div> ......
var vm = new Vue({
el: '#app',
data: {
t1: 'title1',
t2: 'title2'
}
});