目录
mixin指令-减少data、methods、钩子的重复(mixin可以理解为复制)
extends继承 减少重复,和mixin基本一样,比mixin更抽象的封装
1.搭建项目
使用Vue/cli工具
安装:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
查看安装版本:
vue --version
运行以下命令来创建一个新项目:
vue create hello-world
浏览器实时预览:
yarn serve
2.如何使用Vue实例
2.1从HTML 中得到视图
从CDN中应用vue.js或vue.min.js 即可做到。
2.2用js构建视图
使用vue.runtime.js,这种方法不方便但是体积小,用户体验更好,要麻烦就麻烦在开发者这边,用户体验第一。
2.3vue-loader
.vue文件翻译成h构建方法
但是HTML只有一个div#app,SEO不友好。
3.Vue的options属性
封装:const vm = new Vue(options)
options五类属性:数据、DOM、生命周期钩子、资源、其他。
入门属性
- el-挂载点
与$mount有替换关系
- data-内部数据
支持对象和函数,优先使用函数(*避免两个或多个组件共用data的问题,使用函数是每次调用data得到一个data)
new Vue({
data(){
return{
n:0
}
},
template:`
<div class="red">
{{n}}
<button @click="add">+1</button>
</div>
`,
methods: {
add(){
this.n += 1
}
}
}).$mount(#'app')
- methods-方法
事件处理函数或者普通函数
- components
Vue组件,注意大小写。
推荐引入方式:
import Demo from './Demo.vue' //其他组件引入
new Vue({
components: {
Frank:Demo //老手这样写 Demo:Demo 缩写为Demo
}
data(){
return{
n:0
}
},
template:`
<div class="red">
{{n}}
<button @click="add">+1</button>
<Frank/> //<Demo/>
</div>
`,
methods: {
add(){
this.n += 1
}
}
}).$mount(#'app')
- 四个钩子
- created-实例出现在内存中
- mounted-实例出现在页面中
- update-实例更新了
- destroyed-实例消亡了
props-外部属性
main.js文件:
import Demo from './Demo.vue'
new Vue({
components:{Demo},
data(){
return{
n:0
}
},
template:`
<div>
{{n}}
<Demo :message="n" :fn="add"/>
</div>
`,
methods: {
add(){
this.n += 1
}
}
}).$mount(#'app')
Demo.vue组件:
<template>
<div>
这里是Demo内部
{{message}}
<button @clink='fn'>call fn</button>
</div>
</template>
<script>
export default {
props: ['message','fn']
}
</script>
<style scoped>
.red{
color:red
}
</style>
options.data 深入理解
- get和set
let obj0 = {
姓: "高",
名: "圆圆",
age: 18
};
// 需求一,得到姓名
let obj1 = {
姓: "高",
名: "圆圆",
姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?
// 需求二,姓名不要括号也能得出值
let obj2 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求二:" + obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数,仅此而已。
// 需求三:姓名可以被写
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.slice(1)
},
age: 18
};
obj3.姓名 = '高媛媛'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
// 总结:setter 就是这样用的。用 = xxx 触发 set 函数
let data0 = {
n: 0
}
// 需求一:用 Object.defineProperty 定义 n
let data1 = {}
Object.defineProperty(data1, 'n', {
value: 0
})
console.log(`需求一:${data1.n}`)
// 总结:这煞笔语法把事情搞复杂了?非也,继续看。
// 需求二:n 不能小于 0
// 即 data2.n = -1 应该无效,但 data2.n = 1 有效
let data2 = {}
data2._n = 0 // _n 用来偷偷存储 n 的值
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value < 0) return
this._n = value
}
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)
// 抬杠:那如果对方直接使用 data2._n 呢?
// 算你狠
// 需求三:使用代理
let data3 = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问
function proxy({data}/* 解构赋值,别TM老问 */){
const obj = {}
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
// 因为我怕你们看不懂
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return
data.n = value
}
})
return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`需求三:${data3.n},设置为 1 成功`)
// 杠精你还有话说吗?
// 杠精说有!你看下面代码
// 需求四
let myData = {n:0}
let data4 = proxy({ data:myData }) // 括号里是匿名对象,无法访问
// data3 就是 obj
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n},设置为 -1 失败了吗!?`)
// 我现在改 myData,是不是还能改?!你奈我何
// 艹,算你狠
// 需求五:就算用户擅自修改 myData,也要拦截他
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问
function proxy2({data}/* 解构赋值,别TM老问 */){
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
// 因为我怕你们看不懂
let value = data.n
Object.defineProperty(data, 'n', {
get(){
return value
},
set(newValue){
if(newValue<0)return
value = newValue
}
})
// 就加了上面几句,这几句话会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return//这句话多余了
data.n = value
}
})
return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)
// 这代码看着眼熟吗?
// let data5 = proxy2({ data:myData5 })
// let vm = new Vue({data: myData})
// 现在我们可以说说 new Vue 做了什么了
Object.defineProperty
- 可以给对象添加属性 style
- 可以给对象添加getter/setter
- getter/setter用于对属性的读写进行监控
Vue的data是响应式,Vue2通过Object.defineProperty来实现数据响应式
Vue.set和this.$set
- 新增key
- 自动创建代理和监听(如果没有创建过),作用于数组时不会自动监听。
- 出发ui更新(不会立刻更新)
举例:
this.$set(this. object,'m',100)
data中有数组
可以用this.array.push(),push API自带set属性
进阶属性
computed和watch
computed-计算属性
- 不需要加括号
- 他会根据依赖是否变化来缓存
示例:
// 引用完整版 Vue
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
let id = 0;
const createUser = (name, gender) => {
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
displayUsers: []
};
},
created() {
this.displayUsers = this.users;
},
methods: {
showMale() {
this.displayUsers = this.users.filter(u => u.gender === "男");
},
showFemale() {
this.displayUsers = this.users.filter(u => u.gender === "女");
},
showAll() {
this.displayUsers = this.users;
}
},
template: `
<div>
<div>
<button @click="showAll">全部</button>
<button @click="showMale">男</button>
<button @click="showFemale">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
// 引用完整版 Vue
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
let id = 0;
const createUser = (name, gender) => {
id += 1;
return { id: id, name: name, gender: gender };
};
new Vue({
data() {
return {
users: [
createUser("方方", "男"),
createUser("圆圆", "女"),
createUser("小新", "男"),
createUser("小葵", "女")
],
gender: ""
};
},
computed: {
displayUsers() {
const hash = {
male: "男",
female: "女"
};
const { users, gender } = this;
if (gender === "") {
return users;
} else if (typeof gender === "string") {
return users.filter(u => u.gender === hash[gender]);
} else {
throw new Error("gender 的值是意外的值");
}
}
},
methods: {
setGender(string) {
this.gender = string;
}
},
template: `
<div>
<div>
<button @click="setGender('') ">全部</button>
<button @click="setGender('male')">男</button>
<button @click="setGender('female')">女</button></div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
</ul>
</div>
`
}).$mount("#app");
watch-监听
- 一旦data发生变化就会执行函数
- options.watch用法
- this.$watch用法
- deep,immediate含义
示例:实现撤销功能
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
data: {
n: 0,
history: [],
inUndoMode: false
},
watch: {
n: function(newValue, oldValue) {
console.log(this.inUndoMode);
if (!this.inUndoMode) {
this.history.push({ from: oldValue, to: newValue });
}
}
},
// 不如用 computed 来计算 displayName
template: `
<div>
{{n}}
<hr />
<button @click="add1">+1</button>
<button @click="add2">+2</button>
<button @click="minus1">-1</button>
<button @click="minus2">-2</button>
<hr/>
<button @click="undo">撤销</button>
<hr/>
{{history}}
</div>
`,
methods: {
add1() {
this.n += 1;
},
add2() {
this.n += 2;
},
minus1() {
this.n -= 1;
},
minus2() {
this.n -= 2;
},
undo() {
const last = this.history.pop();
this.inUndoMode = true;
console.log("ha" + this.inUndoMode);
const old = last.from;
this.n = old; // watch n 的函数会异步调用
this.$nextTick(() => { //可以理解为setTimeout
this.inUndoMode = false;
});
}
}
}).$mount("#app");
deep:
deep意思是监听object的时候是否往深了看。
示例:object.a变了,object算不算也变了。需要答案是变了,deep:true;需要答案是不变,deep:false。
// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
data: {
n: 0,
obj: {
a: "a",
b: "b"
}
},
template: `
<div>
<button @click="n += 1">n+1</button>
<button @click="obj.a += 'hi'">obj.a + 'hi'</button>
<button @click="obj = {a:'a'}">obj = 新对象</button>
</div>
`,
watch: {
n() {
console.log("n 变了");
},
obj: {
handler(){
console.log("obj 变了");
},
deep:true
},
}
}).$mount("#app");
语法:vm.$watch('xxx', fn, {deep: .., immediate: ..},其中‘xxx’可以改为一个返回字符串的函数。
watch禁止使用箭头函数,this为全局对象window或global
watch:{
n:()=>{
this.xxx
}
}
面试题:computed和watch区别
- computed是计算属性,watch是监听的意思。
- computed计算出一个值,这个值被调用的时候不需要加括号,根据依赖会自动缓存,依赖不变的话就不会重新计算。
- watch 中 immediate-第一次渲染的时候是否要执行这个函数,deep-监听对象是否看里边的属性的变化。某个属性变化了就执行一个函数。
绑定属性
- v-bind
<img v-bind:src='x'>绑定src =>简写为 <img :src='x'>
绑定对象:
<div :style="{border:;'px solid red',height:100}">这里的100px可以写成100</div>
绑定事件
- v-on:事件名
缩写<button @click="add">+1</button>
条件判断
- v-if
<div v-if="x>0">
x大于0
</div>
<div v-else-if="x===0">
x=0
</div>
<div v-else>
x小于0
</div>
循环
- v-for 后边注意加 :key
<ul>
<li v-for="(value,name) in obj" :key="name">
属性名:{{name}},属性值{{value}}
</li>
</ul>
显示、隐藏
- v-show
<div v-show="n%2===0"> n 是偶数</div>
修饰符
.sync修饰符
Vue 规则:组件不能修改 props 外部数据
Vue 规则:this.$emit 可以触发事件,并传参
Vue 规则:$event 可以获取 $emit 的参数
示例学习:
App.vue:
<template>
<div class="app">
App.vue 我现在有 {{total}}
<hr>
<Child :money.sync="total"/>
//逻辑为 <Child :money="total" v-on:update:money="total=$event"/> 简写成了.sync,实际是个语法糖
</div>
</template>
<script>
import Child from "./Child.vue";
export default {
data() {
return { total: 10000 };
},
components: { Child: Child }
};
</script>
<style>
.app {
border: 3px solid red;
padding: 10px;
}
</style>
Child.vue:
<template>
<div class="child">
{{money}}
<button @click="$emit('update:money', money-100)">
<span>花钱</span>
</button>
</div>
</template>
<script>
export default {
props: ["money"]
};
</script>
<style>
.child {
border: 3px solid green;
}
</style>
directive指令-减少DOM操作的重复
mixin指令-减少data、methods、钩子的重复(mixin可以理解为复制)
extends继承 减少重复,和mixin基本一样,比mixin更抽象的封装
provide、inject(提供、注入)
作用:大范围的data和methods等共用。
vue表单和v-model
基本用法:input/textarea/checkbox/radio/select/form
修饰符:.lazy/.number/.trim
v-model:面试官问Vue的双向绑定,其实就是在问v-model
但是实际上v-model是v-bind:value和v-on:input的语法糖