VUE 实例
在我们使用 vue 之前我们需要事先得到 一个 vue 实例对象,我们需要传递一个对象用于描述你的 vue 实例。
const app = new Vue({});
DOM 相关
-
el
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例
const app = new Vue({ el: "#app", // el: document.getElementById("app"), });
-
template
一个字符串模板作为 Vue 实例的标识使用。模板将会替换挂载的元素。你可以回忆一下之前学过的 innerHTML 的作用。
const app = new Vue({ el: "#app", template: "<span>hello world</span>", });
-
render
字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。如果实例化 Vue 中同时传递了 template 和 render 那么,template 会直接忽略【因为其实 template 也会走 render 的流程】。
// render 函数中通过 createElement 创建虚拟节点(所谓虚拟节点就是使用js对象模拟DOM节点,然后在特定的方法中再解析成为真正的节点。这样做的好处是减少了重绘和回流),写法相对而言比较固定。 //createElement 中需要传递三个参数。第一个是你需要渲染什么样的节点。第二个参数是该节点的描述,第三个是该节点的子节点数组。 const app = new Vue({ el: "#app", render: function(createElement) { return createElement( // 要渲染的标签名称 "div", // 该标签的描述 { // 可以写内置样式 style: { color: "red", border: "1px solid black", }, // 可以指定 class class: { cls1: true, cls2: false }, // 普通的 HTML 特性 attrs: { id: 'foo', name:"foo" }, // 主要用于父子组件通讯 props: { }, // 事件监听 on:{ // 指定 作用域,同时传值 click: handle.bind(window, 3), } // 仅用于组件,用于监听原生事件,对于使用 `vm.$emit` 触发的事件不敏感。 nativeOn: { click: handle.bind(window, 3), }, // 自定义指令 directives: [], // 插槽名称 slot:"", // 特殊属性 key: '', ref: '', }, // 该标签的子节点数组 [ "text", // 文本节点直接写就可以 createElement( "div", { style: { color: "blue", border: "1px solid black", }, }, "hahah" ), ] ); }, });
给已有的对象数据添加新的属性
data: {
title: "test",
user: {},
},
// 下面两种方式都能监听数据变化
Vue.set(app.user, 'gender', '男');
// 实例.$set 是 Vue.set 的别名
app.$set(app.user, 'gender', '男');
实例属性
const app = new Vue({
el: "#root",
template: "<div>{{title}}</div>",
data: {
title: "测试数据",
},
// 当然如果是后面再去添加 watch,还需要手动删除监听
/*
const uw = app.$watch("title",(newVal,oldVal)=>{})
uw() // 只需要执行一遍返回的函数,就不再监听 title 的变化
*/
watch: {
title(newVal, oldVal) {
// ...
},
},
});
console.log(app.$data); // data 的部分
console.log(app.$el); // 挂载节点
console.log(app.$options); // 传入Vue的整个对象【包含默认属性】在这里面对 data 重新赋值是无效
app.$options.render = (h) => {
// 那么当数据发生变化的时候,在下一次渲染的时候会更新视图
return h("div", {}, "test");
};
console.log(app.$root === app); // true
// 自定义事件监听
app.$on("someEventName", (...args) => {
// do something
});
/*
// 只监听一次
app.$once("someEventName", (...args) => {
// do something
});
*/
// 在适当的时候触发事件【常用于父子组件通讯】
app.$emit("someEventName", 1, 2);
// 如果在 data 中没有定义属性,那么在后面新增属性的时候并不会监听变化
data: {
obj: {
}
}
app.obj.newKey = 123;
// 可以采取强制组件渲染的方式【会降低性能】
app.$forceUpdate();
/*
// 当然更好的方式是
app.$set(app.obj,"newKey",123);
*/
// 删除对象属性 app.$delete(app.obj, "newKey"); // 如果是采用 直接 delete 会导致内存溢出【 reactive 还存在 】
/*这里直接引入官网的示例*/
// app.$nextTick
methods: {
// ...
example: function () {
// 修改数据
this.message = 'changed'
// DOM 还没有更新
this.$nextTick(function () {
// DOM 现在更新了
this.doSomethingElse()
})
}
}
生命周期
// 先执行了 init 【 new Vue 之后会自动调用 】先去执行了 beforeCreate
beforeCreate(){
// 这里只是事件已经初始化好了,数据部分还没有
// 这里不要修改数据,因为还没有 reactive
}
// 然后在 init 的注入和 reactive 时会调用 created
created(){}
beforeMount(){}
// render function 会在这之间执行
render(){}
// render 出错的时候,但是只捕获本组件的错误,子组件中的错误不会被捕获
renderError(){
// 一般只用于开发环境
}
mounted(){}
beforeUpdate(){}
updated(){}
/*
// 一般在 keep-alive 组件的活跃状态和非活跃状态时触发
*/
activated(){}
deactivated(){}
/*
// 一般在需要销毁一个组件实例的时候使用
app.$destroy()
*/
beforeDestroy(){}
destroyed(){}
// 会向上冒泡,捕获错误,也可以用于正式环境
errorCaptured(){}
数据绑定
new Vue({
// 可以直接使用 js 的内置对象和方法
template: `
<p>{{Date.now()}}</p>
<p>{{a.join('')}}</p>
<p>{{fn()}}</p>
`,
data: {
a: [1, 2, 3],
},
methods: {
fn(arr) {
// 其实更好的是通过 computed
return arr.join("");
},
},
});
computed
computed 是经过缓存处理的,针对于计算量比较大的操作而言比较合适
computed:{
val(){
return ...
}
}
// or
computed:{
val:{
get(){
return ...
}
// 当然这是不推荐的方式【computed中最好只用于处理数据之后返回,而不要在这里进行数据的变更】
set(newVal){
//...
}
}
}
watch
主要的运用场景是,监听到某个数据的变化,然后执行某个指定的操作(比如 ajax 等)
watch: {
// 这是 data 中的数据项
// 在一开始的时候,数据没有变化,这里也就不执行,等待数据变化才会执行
input(newVal, oldVal) {
console.log("handle:", newVal, oldVal);
},
},
// 当然上面的 watch 只是一种简写形式,其实它的内部也会编译成为下面中形式
watch: {
// 这是 data 中的数据项
input: {
handler(newVal, oldVal) {
console.log("handle:", newVal, oldVal);
},
// 是否立即执行 handler ,如果没有此项就表明需要等待 input 数据项变化的时候才执行
immediate: true,
}
},
// 监听对象属性的变化
watch: {
obj: {
handler(newVal, oldVal) {
console.log("handle:", newVal, oldVal);
},
// 当然这个配置的开销会比较大,毕竟要去监听对象中每一层每一个属性的变化
deep: true, // 是否深度监听
}
},
// 如果只想监听对象中某一个属性的变化
watch: {
// 比如这里只监听 obj 对象中的 a 属性的变化,这样的性能开销比较小
'obj.a': {
handler(newVal, oldVal) {
console.log("handle:", newVal, oldVal);
},
}
},
指令系统
new Vue({
template: `
<div v-text="title"></div>
`,
/*
// 给变量前面加一点其他的信息
template: `
<div v-text="'title:'+title"></div>
`,
*/
/*
// 其实更好的运用场景是根据用户的权限动态的展示界面功能
template: `
<div>
<span v-if="level==1">level-1</span>
<span v-else-if="level==2">level-2</span>
<span v-else >level-3</span>
</div>
`,
*/
/*
data:{
arr:[1,2,3],
obj:{
a:"111",
b:"222",
c:"333",
}
}
template: `
<div>
<ul>
<!-- 这里加 :key 只是为了在下一次渲染的时候,如果在缓存中已经存在了相同的 key 值那就直接拿取缓存的数据,不需要再次渲染,减少性能开销 -->
<!-- 而且这里最好不要用 index 下标作为 key 值,因为一旦数组排序以后可能会导致一个错位的缓存数据 -->
<li v-for="(item,index) in arr" :key="item"></li>
</ul>
<ul>
<li v-for="(val,key,index) in obj"></li>
</ul>
</div>
`,
//////////////////////////////////////////////////////////
data: {
arr: [1, 3, 4],
picked: "a",
},
// 这样只有a 和 c 会被选中,:value 和 value 的区别是 后者是单纯的字符串,前者会解析数值。
template:`
<div>
a<input type="checkbox" :value="1" v-model="arr"/>
b<input type="checkbox" :value="2" v-model="arr"/>
c<input type="checkbox" :value="3" v-model="arr"/>
<hr/>
单选:d<input type="radio" value="a" v-model="picked"/>
e<input type="radio" value="b" v-model="picked"/>
</div>
`
*/
//////////////////////修饰符
/*
<input v-model.number="msg"/>
<input v-model.trim="msg"/>
<input v-model.lazy="msg"/> 只有在失去焦点时才会触发 onchange
*/
/*
只会数据绑定的内容只会执行一次,再有数据变化不会更新视图
<div v-once>{{msg}}</div>
*/
});
- 至于为什么组件内的 data 需要是一个 function
因为组件可能会复用,如果复用组件,那么多个组件就会使用同一个数据对象,造成污染,所以需要在组件中 data 以 function 的形式存在
let app = new Vue({
components: {
T: {
// props: ["test", "str"], // 这是一种不太严谨的写法
props: {
test: {
type: Boolean,
required: true,
default: true,
/*
其实 required: true 之后default也可以直接不要,如果需要default 而且如果是一个对象那么最好也是直接用函数形式返回一个对象,避免组件复用数据污染
default(){
return {}
}
*/
},
str: String,
other: {
validator(value) {
// ... 进行一波检测
// 如果符合
return true;
},
},
},
template: `<h1 v-show="test">{{str}}</h1>`,
},
},
data: {
arr: [1, 3, 4],
picked: "a",
},
mounted() {
// 取出名称为 t1 的组件实例
console.log(this.$refs.t1);
},
template: `
<div>
00
<T ref="t1" :test="true" str='一级标题'>1</T>
</div>
`,
}).$mount(root);
组件扩展
const com1 = {
props: {},
template: "",
data() {},
};
const com2 = {
extends: com1,
data() {
return {};
},
methods: {},
};
new Vue({
components: {
com2: com2,
},
templates: `<com2></com2>`,
});
插槽
const com1 = {
template: `
<div>
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot name="content">
<slot name="msg"></slot>
</slot>
<slot name="test" :val="val" abc="abc"></slot>
</div>
</div>
`,
data() {
return {
val: "com1 Data",
};
},
};
new Vue({
data: {
val: "Vue Data",
},
components: {
com1: com1,
},
template: `
<div>
<com1>
<h1 slot="header">header</h1>
<h1 slot="content">
content
<p slot="msg">{{val}}</p>
</h1>
<!-- props 是可以自定义名称的 -->
<p slot="test" slot-scope="props">{{props.val}}-{{props.abc}}</p>
</com1>
</div>`,
});
render function
const com1 = {
template: `
<div>
<slot name="test" :val="val"></slot>
</div>
`,
data() {
return {
val: "com1 Data",
};
},
};
new Vue({
data: {
val: "Vue Data",
},
components: {
com1: com1,
},
// template: `
// <div>
// <com1 ref="com1">
// <p slot="test" ref="p1" slot-scope="props">hello</p>
// </com1>
// </div>`,
// 上面的 template 会走下面的流程
// 而这里的 createElement 其实就是在创建虚拟 DOM,存储在内存中,
// 会跟真正的 DOM 结构进行对比,如果对比的结果是需要更新原来的DOM结构,
// 就会将 虚拟 DOM 转换成为真正的 DOM
render(createElement) {
return createElement(
"com1",
{
ref: "com1",
},
[
createElement(
"p",
{
slot: "test",
ref: "p1",
"slot-scope": "props",
},
"hello"
),
]
);
},
});