vue是一套用于构建用户界面的渐进式框架。
构造函数方式创建vue实例
let vm = new Vue(options);//options是一个对象,用来配置选项
配置选项
- el : 给当前实例设定一个管辖范围,不可以直接写成body或者html,因为script标签在body里面,可能会把script标签操作.
- data : 当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
- template : 模板,会用其属性值替换el管辖的结构
- methods : 一般函数都放在这里
- computed : 计算属性,依赖其他属性的属性
- filters : 过滤,语法 : 值|过滤器(过滤器是一个函数)
- 生命周期钩子函数(created,mounted,updated…)
let vm = new Vue({
el: "#app",
data: {
name: "贾",
age: 21,
sex: 1
}
});
//对应页面Vue数据
<div id="app">
{{name}}{{age+1}}{{sex?"男":"女"}}
</div>
{{name}}{{age+1}}{{sex?"男":"女"}}{{fn()}}{{value.replace(/,/g,'')}}小胡子语法 : 支持vue数据,简单的运算符,三元运算,函数运行,正则等.
小胡子语法里面不能写多个数据,可以多个数据进行拼接或者运算,只显示第一个数据,或者多个数据运算得出的第一个数据
> 引用数据类型进行数据绑定
> - 对象中的属性初始化了之后才会有get和set
> - 数组绑定完成后,后面如果需要操作数组来实现页面的改变,必须改变数组的地址,重新赋值新的数组(数组中所有直接操作原数组的方法都可以刷新页面:push / pop / unshift / shift / reverse / sort / splice)
> - data : 加载data中的数据可以通过vm.$data.xxx获取,也可以通过vm.xxx获取
> - 初始化对象中的属性vm.$set(vm.data,"name","666")
```javascript
let vm = new Vue({
el: "#app",
data: {ary:[1,2,3,4]}
});
//vm.push(100)如果加上push,页面会重新刷新,如果不加,下面数组操作不会起到作用
vm.ary[2] = 300;
vm.ary[1] = 0;
双向数据绑定
是通过Object.defineProperties(data,{})或者Proxy 拦截实现的.
一段实现了一丢丢丢双向绑定的代码
let app = document.getElementById("app"),
inputs = app.getElementsByTagName("input"),
nodeList = [...app.children].filter(item => item.tagName !== "INPUT"),
data = {name: "贾腾达"},
cloneList = nodeList.map(item => item.cloneNode(true));
console.log(cloneList);
for (let val of inputs) {
if (val.getAttribute("v-model")) {
val.value = data[val.getAttribute("v-model")];
}
val.oninput = function () {
data[val.getAttribute("v-model")] = this.value;
};
}
nodeList.forEach((item, index) => {
if(/\{\{\w+\}\}/.test(item.innerHTML)){
item.innerHTML = item.innerHTML.replace(/\{\{(\w+)\}\}/, (...arg) => data[arg[1]]);
}
});
Object.defineProperties(data, {
name:{
set(val){
for(let item of inputs){
if (item.getAttribute("v-model") === "name") {
item.val = val;
}
}
cloneList.forEach((item, index) => {
nodeList[index].innerHTML = item.innerHTML.replace(/\{\{name\}\}/g, () => val);
});
}
}
});
事件绑定
<div onclick="fn(event)">1111111</div>
<!--不使用Vue,event 实参事件对象 window.event-->
<div id="app">
<!--绑定事件@事件类型="xxx"-->
<div @click="fn1">222</div>
<!--fn不写括号默认执行的时候就会传一个事件对象-->
<div @click="fn2($event,'aa')">222</div>
<!--如果用到其他参数,还想用事件对象的话必须手动传$event-->
<div @click="fn3('a')">222
<i @click.stop="fn">iiiiiii</i>
</div>
<input type="text" @keyup.enter="get">
<!--
.stop:阻止时间冒泡
.prevent:阻止默认行为
.capture:捕获阶段发生
.self:自己作为事件源的时候才会发生
.once:只执行一次
.passive:在移动端的滚动事件会用到,会提高性能,不阻止默认行为
.enter/.tab/.delete (捕获“删除”和“退格”键)
.esc/.up/.space....
</div>
--------
let vm = new Vue({
el: "#app",
data: {},
methods: {
//函数放在这里面
fn1(e) {console.log(e);},//MouseEvent
fn2(e,a) {console.log(e,a);},//MouseEvent,'aa'
fn1(e) {console.log(e);},//'a'
get() {
//this表示当前实例
console.log(this);
}
}
})
指令
v-text=”text” : 所有数据只看做文本
v-html=”text” : 识别标签
v-model=”text” : 双向数据绑定(一般都是表单元素会有双向数据绑定)checkBox 中只要有了v-model,checked就不起作用了, 实现多选 : 每一个checkBox的v-model都是同一个数据,而且是一个数组,只要选中checkBox,就会将其value值加在数组中,如果没有value值就是null;
radio单选按钮,v-model绑定相同的数据,这个数据存放的是被选中的value值
男
女
select实现单选 v-model是一个值,存放的是选中的option的value值,没有value值就是option的内容
select实现多选,给select加一个属性multiple,v-model绑定数据就变成了一个数组,数组中存放的是所有option的value值,没有value值就是option的内容
> v-once : 初始的时候渲染一次,以后数据改变也不会再次渲染
```javascript
<p v-text="text"></p>//<p>哈哈哈哈哈</p>
<p v-html="text"></p>//哈哈哈哈哈
<input type="text" v-model="text">//输入框里面内容:<p>哈哈哈哈哈</p>
<p v-once>{{text}}</p>//<p>哈哈哈哈哈</p>
//再次改变text数据的值,前三个内容都会跟着改变,而v-once以后再也不会改变
//js
let vm = new Vue({
el:"#app",
data:{text:"<p>哈哈哈哈哈</p>"}
})
v-for : 加到想要循环的元素上
{{item}}
value(对象):键值对有几对循环几次,item是属性值 value(数字):数字是几,循环几次,负数报错,item一次递增 value(数组):数组也是对象 value(null&undefinde&布尔值):不循环,不报错 value(字符串):循环value.length次,item是每一次循环的字符 ``` > v-bind : 动态绑定属性,绑定的是vue中的数据,也可以直接给属性名前面加上`:`(冒号 : 新版本快捷使用) > - 如果绑定的是class类名 > - 如果绑定的是一个对象,根据每一个属性名对应的属性值判断拿不拿这个属性名作为类名。 > - 如果绑定的是一个数组,可以用来增删类名,很方便,而且可以通过索引取出某项值 > - 如果绑定的是一个对象,让类名显示去除很方便 > > - style : 要么绑定的是一个对象,要么绑定的是一个数组,而且数组的每一项都是一个对象 ```javascript //可以省略v-bind,直接加一个冒号### 自定义指令
```javascript
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
//-------------局部自定义指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
filters : 过滤器,语法 : 值|过滤器(过滤器是一个函数)
- 这个过滤器是处理管道符前面的值的,将前面的值按照过滤器定义的方式过滤
- 过滤器会默认把|(管道符)前面的值当做第一次参数传给里面的函数
- 如果不需要传其他参数,可以省略括号,默认执行
- 如果需要传参,第二个参数开始才是我们传的参数,第一个依然是target
- 可以多次使用管道符,前面一个处理完了,会把结果当做参数传递给下一个管道符后面的函数
computed : 计算属性
- 在这里面写的属性不要在data里面重复写
- 跟data中的数据一样,可以通过vm.数据获取或者修改
- 如果别的属性改变会影响到这个属性,可以通过计算属性来操作
- get必须有,set可以没有,一般v-model双向数据绑定的有set,其他的只有get
- 如果只有get,可以简写成一个函数的形式,但是它还是一个属性
- 依赖的属性不能出现什么差错(比如没有初始化),那么这个计算属性也会不起作用
- set里面会默认传一个参数与,就是设置的属性值
- get的返回值,就是展示的数据值
- 第一次获取的时候,依赖属性没有改变,也会默认执行一次
- 不支持异步,必须有get,而且get里面必须return
watch : 监听data里面的属性
- 第一次默认不会执行,每一次执行会给其传入两个参数,新值和旧值
- 如果想让其第一次执行一下,可以加一个属性immediate : true;
- 如果监控的属性值是一个对象,那么监控的值是这个对象的地址,如果地址不变,不管里面的内容是否改变,都不会被监控到,如果想要监控对象地址指向空间的变化,需要给其加上一个属性deep : true;
computed: {
isAll: {
get() {
console.log(1);
return this.is;
},
set(val) {
//val 设置的值
this.isAll = this.is;
}
},
watch:{
obj:{
handler(newVal,oldVal){
console.log(newVal, oldVal);
},
deep: true//如果想监控对象里面值的改变,需要加上这个属性
}
}
Vue标签
- vue中提供的自定义标签,也叫组件
transition ,transition-group : 使用这两个标签包起来的内容可以加上一些自定义的动画效果
- 一般我们结合animate.js或者其他插件使用
- transition标签里面只能放一对标签,transition-group里面能放多个标签
<button @click="flag=!flag">切换</button>
<transition enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
<div v-show="flag"><h1>I LOVE YOU</h1></div>
</transition>
<transition-group enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
<div v-show="flag" :key="3"><h1>I LOVE YOU</h1></div>
<div v-show="flag" :key="4"><h1>I LOVE YOU</h1></div>
<div v-show="flag" :key="5"><h1>I LOVE YOU</h1></div>
</transition-group>
生命周期
beforeCreate : 数据初始化之前,在这里获取不到data的值
created : 数据初始化完成,可以操作和获取data中的数据,一般数据请求在这里实现(常用)beforeMound : 挂载之前
mounted : 挂载完成(常用,获取渲染后真实的DOM)beforeUpdate : 数据更新之前
updated : 更新完成(这两个函数一般会用watch代替)beforeDestroy : 销毁之前
destoryed : 销毁完成
vm.$destroy() : 手动销毁,不会销毁之前的内容,渲染好页面也保留下来了,但是后面实例就不再起作用了,
根据官方流程图总结
vm.$mount("#app");//跟el:"app",一样,没有el的时候才会看 $mount
let vm = new Vue({
el:"#app",//先看一下有没有el,没有的话会走vm.$mount
template:"<div></div>",//如果有template,就相当于直接换掉#app,里面的内容只能有一个根元素包裹,否则会报错
data:{a:"a"},
beforeCreate(){console.log(this.a)}//undefined
created(){console.log(this.a)}//"a"
beforeMound(){}//判断渲染模板正确与否
mounted(){}//挂载完成,视图渲染
beforeUpdate(){}//数据更新之前
updated(){}//数据更新完成
beforeDestroy(){}//销毁之前
destroyed(){}//销毁完成
})
` //-----------------js let vm = new Vue({ el:"#app", data:{msg:"贾腾达",list:[1,2,3,4,5,6]}, mounted() { //挂载完成了,如果想要获取渲染后好的DOM元素在这里获取 // console.log(this.$refs); //可以通过指定的属性名(ref属性对应的属性值)获取想要的元素 this.list = [1, 2, 3]; //渲染DOM是异步的,这里仍然是渲染之前的 this.$nextTick(() => { //等到DOM渲染完成后执行,通过这种方法拿到更新后的数据 console.log(this.$refs.p.length);//3 }); console.log(this.$refs.p.length);//6 }, updated(){ //在这里面渲染完成 } }) ```ref : vue中提供的获取元素的方法,给想要获取的元素加一个属性ref=”xxx”。会存储到vue实例的$refs里面,$refs是一个对象,里面存储着加ref属性的元素
表现形式:{ref后面的值:元素}({box:div})
可以通过指定的属性名(ref属性对应的属性值)获取想要的元素
如果ref值相同了,只能获取一个,后面的覆盖前面的,因为存储的是一个对象形式,属性不重名,后面属性覆盖前面的
通过循环得到的元素,加上ref,在vm.refs里面得到的值是一个数组,里面存放着循环出的所有的元素
mounted函数解析
只执行一次,一般操作元素都是在这里面操作
为了保证真实性,一般都在外面包一个this.$nextTick(),参数里面传一个函数,里面包着我们写的代码,等到DOM渲染完成后执行,通过这种方法拿到更新后的数据
`<div id="app"> <div ref="box">{{msg}}</div> <div ref="box1">{{msg}}</div> <div ref="box2">{{msg}}</div> <div ref="box">{{msg}}</div> <p ref="p" v-for="item in list">{{item}}</p>
全局组件
- 组件是独立的,独立的生命周期,独立的数据
- 组件中的data必须是一个函数,返回一个对象,里面存储着我们想要的data
- 里面的complate属性存储的是组件的所有结构,有且仅有一个根元素
- 做开发的时候,一般定义组件的名字使用驼峰命名法,使用组件的时候用中杠的形式(中杠后面跟一个小写字母相当于一个大写字母)
- 因为标签名只支持小写,如果组件名有大写字母,会自动转换为小写
<div id="app">
<!--标签只支持小写-->
<vbox></vbox>
<vBox></vBox><!--跟小写的一样,就算写成大写也会编译成小写的-->
<v-box></v-box><!--给大写字母前面加一个中杠,即可识别成大写的-->
</div>
-----------------------JS
Vue.component("vbox", {
template: "<div><h1>{{msg}}</h1></div>",//有且只有一个根元素
data(){//默认执行
return {
msg: "我是一个独立的组件"
}
},
});
局部组件
- 三部曲 : 定义组件,注册组件,使用组件
let component1 = {
template: "<div><h2 @click='fn'>{{val}}</h2></div>",
data(){return{val: "我是组件component1"}},
methods: {fn() {console.log(this);//this是当前组件}
},
};
//注册组件
let vm = new Vue({
el:"#app",
data: {},
components: {
//注册组件
component1,//相当于属性值属性名都是component1
component2:{ template: "<div><h2>我是组件component2</h2></div>"}//如此效果一样,不过内容过长不容易看,一般向component1那样写
}
})
//使用组件
<div id="app">
<!--使用组件-->
<component1></component1>
<component2></component2>
</div>
组件嵌套使用
- 组件里面也有components属性,其子组件只需要在里面注册,就可以在父组件模板中使用
let son = {
template: "<div><h2>{{name}}</h2></div>",
data() {return{name: "son"}}
};
let parent = {
template: "#temp1",
data(){return {name: "parent"};},
components:{son}
};
let vm = new Vue({
el:"#app",
data: {},
components: {
parent
}
})
//使用组件
<div id="app">
<parent></parent>
</div>
<template id="temp1">
<div><h2>{{name}}</h2><son></son></div>
</template>
组件之间的数据传递
父组件给子组件传递数据
- 使用props属性获取组件在使用的时候从父组件获取的数据
- props里面放着所有的属性(属性名),全部都是字符串
- 父传子还能进行属性校验,props属性值是一个对象.
<div id="app">
<parent></parent>
</div>
<template id="parent">
<div>
<son :k="key" :v="value" @change="change"></son>
<h1>I'm parent</h1>
<button @click="point">point me </button>
</div>
</template>
<template id="son">
<div>
<button @click="sonPoint">Point me </button>
<h2>I'm son</h2>
</div>
</template>
let son = {
template:"#son",
props:{
k:{
type:String,//类型
required:true,//是否必填
default:"666666"
},
v:{
validator(val){
//自定义校验,返回true则通过校验
return typeof val ==="string";
}
}
}
}
let parent = {
template:"#parent",
components:{son},
data(){return{key:"jia",value:"tengda"}},
}
let vm = new Vue({
el:"#app",
data:{},
components:{
parent
}
})
子组件传递给父组件
- 利用$emit(“订阅的事件”,给订阅的事件传参数)
<div id="app">
<h1>{{money}}</h1>
<child :m="money" @change-money="change"></child>
<!--订阅了一个自定义事件 change-money 由child控制的-->
</div>
<template id="child">
<div>
<h2>{{m}}</h2>
<button @click="getMore">more money</button>
</div>
</template>
- 此处给child绑定属性,:m属于child的属性,money是父组件app中的数据,@change-money(不能用驼峰命名,大写字母前面可以加中杠),是子组件的自定义事件,change是父组件的方法
- $emit相当于发布订阅中的发布,通知订阅的事情执行,第一个参数是订阅的方法,第二个参数是给方法传参数
let child = { template: "#child", props: ["m"], methods: { getMore() { //发布之前订阅的事件 //第二个参数就是给之前订阅的事件传参数用的 this.$emit("change-money", 100); } } }; let vm = new Vue({ el: "#app", data: { money: 10000 }, components: {child}, methods: { change(money) { //自己修改自己data,让子组件控制这个函数执行 this.money = money; } } })
父组件操作子组件
<div id="app">
<button @click="hide">point me</button>
<!--给组件加一个ref属性-->
<child1 ref="child1"></child1>
</div>
-----------
console.log(this.$refs.child1);//child1组件,通过父组件的$refs获取里面的子组件
组件的生命周期
- 组件的数据修改只会触发自己的beforeUpdate,updated,不会触发父组件的生命周期
- 在父组件的mounted中加一个this.$nextTick(),将获取自己活着是子组件的元素放在里面,保证拿到的是最新渲染后的
执行顺序
vm.created>vm.beforeMount>child.created>child.beforeMount>
child.amounted>vm.mounted(如果child组件的数据修改)>child.beforeUpdate>child.updated>vm.$nextTick()(页面修改)
{{msg}}
{{msg}}
component标签
- component标签里面只能放一个组件,组件之间切换的时候会销毁之前的组件,重建新的组件,这样很浪费性能
- keep-alive标签里面包住component标签,这样组件不会销毁,只是缓存一下(类似缓存,不是缓存)
- is是写在component标签里面的属性,is后面的属性名是哪个组件名,就显示哪个组件
- component标签大小写都可以,浏览器最后都会解释成小写,因为标签名都是小写的
<!--Component is="这里是什么组件就显示什么组件"--> <!--Component 首字母大小写都可以--> <input type="radio" v-model="sel" value="com1">com1 <input type="radio" v-model="sel" value="com2">com2 <!--<Component is="com1"></Component>--> <!--Component 里面只能放一个组件,组件之间切换的时候就会销毁之前的组件,重建新的组件,这样会浪费性能--> <!--涉及到组件切换的时候,用keep-alive,这样组件不会销毁,只是缓存一下--> <keep-alive> <Component :is="sel"></Component> </keep-alive> //-----------------js let com1 = { template: "<div>我是com1</div>", created() { // debugger;//点击com1触发 }, beforeDestroy() { // debugger;//点击com2触发 } }; let com2 = { template: "<div>我是com2</div>" }; let vm = new Vue({ el: "#app", data: {sel:""}, components: {com1,com2 } }) 如果component标签外面不加keep-alive标签,组件会不停的销毁重建,上面的debugger不管点击哪一个,都会有一个重建,一个销毁,如果加了以后,就不会销毁,会缓存起来
slot : 插槽
- 组件中的标签加一个属性slot=”
属性名
“,组件中定义的模板的slot对应有name属性,放上对应的”属性名
“,两个属性名相同- 如果模板上slot标签没有name属性,就会给其一个默认属性及属性名name=”
default
“,将组件中没有加slot属性的放到里面,但是只能有一个,写多个会给警告,虽然可以实现,但是不易维护- 如果没有匹配到对应的name值,将会显示slot里面的内容,相当于写了一个默认值
孟倩
vue路由 : 根据不同的地址匹配不同的模块(组件)
- 需要引入vue-router.js
- 路由有两种模式:1.hash模式(默认) 2.history模式
//构造函数方式创建router let router = new VueRouter(); //在Vue实例中注册router let vm = new Vue({el:"app",router})//router:router简写 //创建router实例常用参数 let router = new VueRouter({ mode:"history",//路由模式选择(两种) routes,//注入的路由配置 linkActiveClass: "active"//给router-link标签加选中类名 });
- 只要创建了VueRouter的实例,页面的url地址就会在后面多加一个#/
- 一般分为三步(二三步一般都写在一起)
//1.定义组件 let home = {template:"<h1>home</h1>"}; let list = {template:"<h1>list</h1>"}; //2.路由映射(映射表):路径和组件之间的映射关系 let routes = [ {path:"/home",component:home}, {path:"/list",component:list}, ]; //3.注入路由 let router = new VueRouter({ routes,//routes:routes }); //一般不会写三个步骤,会把后两步写在一起 let router = new VueRouter({ routes: [ {path:"/home",component:home}, {path:"/list",component:list}, ], });
- 在页面中展示 : 用router-view展示,用router-link跳转(to属性)
路由的映射问题 : 首次页面打开,默认路径是”/“,我们让其显示主页,如果用户在url上面改hash值,我们让其显示某页面(比如主页),并且让hash值也重定向成对应页面的hash(或者地址)
//组件还是上面的组件,结构也是上面的结构 let router = new VueRouter({ //mode:"history", routes: [ //首次打开,默认路径是"/",我们让其显示home组件 {path: "/", component: home}, {path: "/home", component: home}, {path: "/list", component: list}, //从上到下依次匹配,如果上面都没有匹配到,就走最后一个,所以星号这个放在最后面 // {path: "/*", component: home},//如果不想重定向,可以这样写 //redirect:重定向,属性值只能放路径,不能放组件名,会把后面hash值改变成这个属性值 {path: "/*", redirect: "/home"} ], });
编程式导航 : 除了使用router-link创建标签a跳转,还可以通过router的实例方法,通过编码来实现.
- 组件都有两个属性 : 1.$router(存放方法) ; 2.$route(存放属性)
方法 : go/back/forward/push/replace…(跟原生history上的方法类似)
- $router.push向history栈中添加一条数据,用户点击back时,回到原来的url
- $router.replace(location) 跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
- $router.go(n) 方法的参数是一个整数,在 history 记录中向前或者后退多少步
home
list
let home = {
template: “#home”,
methods:{
goList() {
this.$router.push(“/list”);
// this.$router.replace(“/list”);
}
}
};
let list = {
template: “#list”,
methods:{
goHome() {
this.$router.replace(“/home”);
},
goHome2() {
this.$router.push(“/“);
}
}
};
/*
* 组件都有两个属性
* $router:放的函数,go/back/forward/push/replace…
* $route:放的属性
*/
let router = new VueRouter({
routes: [
{path: “/“, component: home},
{path: “/home”, component: home},
{path: “/list”, component: list}
]
});
let vm = new Vue({
el: "#app",
data: {},
router,
components: {}
})
### 路由嵌套
```javascript
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/list">列表</router-link>
<router-view></router-view>
</div>
<template id="home">
<h1>首页</h1>
</template>
<template id="list">
<div>
<router-link to="/list/list1">list1</router-link>
<router-link to="/list/list2">list2</router-link>
<router-view></router-view>
</div>
</template>
let home = {template: "#home",};
let list = {template: "#list"};
let list1 = {template: "<h2>list1</h2>"};
let list2 = {template: "<h2>list2</h2>"};
let router = new VueRouter({
routes: [
/* {path:"/",component:home},*/
{path: "/home", component: home},
{
path: "/list",
component: list,
//children:子路由
children: [
//子路由的路径不要写"/"
{path: "list1", component: list1},
{path: "list2", component: list2}
]
}
]
});
let vm = new Vue({el: "#app",data: {},router})