推荐用templete来创建html
渲染函数比模板更接近编译器
本地安装软件:nodejs安装包,找中文网
:表示动态数据,数据来源是数据模型或局部作用域
innerHTML:可获取/设置指定元素标签内的全部html内容——包含标签,所有浏览器都支持
给元素设置内容:element.innerHTML=htmlString
获取内容:document.getElementById("test").innerHTML
innerText:不包含标签,只是文本内容——火狐不支持
一、Vue基础
自己拿数据:this.msg
dom节点拿:{{msg}}基本渲染
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app"> </div>
<!--Vue事件绑定-->
<button @click="changeMsg">点击我改变msg值</button>
光写方法名就行
创建Vue实例对象,与模板(dom节点)绑定
let vm=new Vue({
el:"#app",
data:{ /*Vue实例使用的数据都会定义在data中*/
msg:"hello"
},
methods:{ //函数或方法
changeMsg(){
this.msg='123'; //获取、更改数据模型中的数据
}
}
})
结果:点击按钮后,变成123
页面显示时间,1s动一次
<div id="app">
{{time}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:"#app",
data:{
name:"zhangsan",
time:new Date()
},
methods:{
sayName(){
console.log(this.name);
}
}
});
//Vue实例内的东西一般不会在外部用
console.log(vm); //Object{}
console.log(vm.msg); //hello
vm.sayName(); //zhangsan
//1s改变一次
setInterval(()=>{vm.time=new Date()},1000)
</script>
二、安装
将Vue.js引入项目中的方法:
1、在页面上以CDN包的形式导入——bootcdn官网
制作原型或学习,可以使用最新版本:<script src="https://unpkg.com/vue@next"></script>
生产环境下:推荐链接到一个明确的版本号和构建文件(避免新版本造成的不可预期的破坏)
2、下载JavaScript文件并自行托管
3、使用npm安装(构建大型应用时推荐)——能很好地和类似于webpack模块打包器配合使用
#最新稳定版 $npm install vue@next
4、使用官方的CLI构建一个项目
1、npm、cnpm、yarn——包管理工具(有一个就行)
通过npm装cnpm,npm install -g cnpm
yarn,npm install -g yarn
2、cli——Vue项目模板
快速搭建大型单页面应用的脚手架
单页面应用:仅刷新局部资源,公共资源(js、css等)仅需加载一次,常用于PC端官网、购物网站
只有一个页面,根据url地址展示不同组件(左导航,右组件)
优:前后端分离,快
缺:初次加载耗时多
全局安装vue-cli工具:cnpm install -g @vue/cli
用vue-cli工具创建项目:vue create app
(在哪个文件夹创建就切换到哪个文件夹下)
选Manually select features回车
选Choose Vue version(Babel、TypeScript、Router、Vuex...)
Babel回车
选2.0版本(稳定版本)
选dedicated config files(自己的配置文件)
不保留
就开始下载了——————————
用提示的操作启动项目
显示项目运行在local:http://localhost:8080/
network:http://ip地址:8080/ 访问某个网站或使用代理服务器时,会加上“:8080”端口号
每个.vue文件都是一个组件
public文件夹——~index.html(整个程序的入口文件/首页)
整个body里就一个div id="app"(整个Vue实例绑定的模板)
favicon.ico图标
src文件夹——assets静态资源文件夹:logo.png文件
components文件夹:HelloWorld.vue文件(templete、script、style标签)
把其他组件引到App.vue组件中
~App.vue文件:主组件
~main.js文件:引入了App.vue
将App作为模板手动挂载到到.$mount("#app")(index.html的app中)
.browserslistrc文件
.gitignore文件
babel.config.js文件
package.json文件
README.md文件
三、生命周期
例子1
<div id="app"></div>
new Vue({
el:'#app',
data:{
msg:'hello'
},
//钩子函数
beforeCreate(){
console.log(this.msg, 'beforeCreate'); //undefined,data和methods还没被初始化,无法获取
},
created(){
console.log(this.msg, 'created'); //hello
}
})
例子2
<div id="app">
{{msg}}
<button @click="test">事件测试</button>
</div>
let vm=new Vue({
el:'#app',
data:{
msg:"hello",
stus:[],
},
methods:{
test(){
console.log('点击事件处理程序');
}
},
beforeCreate(){
console.log('创建实例前');
},
created(){
console.log('实例创建好了,可访问数据模型中的数据');
$.ajax({
success(res){
this.stus=res.data;
}
})
},
beforeMount(){
console.log('挂载前');
},
mounted(){
console.log('挂载后');
},
beforeUpdate() {
console.log('更新前');
},
updated() {
console.log('更新好了');
},
beforeDestroy() {
console.log('销毁实例前',this.msg);
},
destroyed() {
console.log('实例销毁完毕',this.msg);
}
})
//测试update
setTimeout(()=>{ //超时调用
vm.msg=123;
vm.$destroy();
},5000)
测试销毁——在控制台改变data里的值
四、渲染
数据渲染:要显示在页面的数据通过js实现而不是直接在HTML中输入数据
1、基本渲染:{{}}
2、循环(列表)渲染:v-for指令(:key唯一标识,避免多次渲染)
数组基本用循环渲染
3、条件渲染:v-if v-else v-show(一会显示、一会儿不显示)
1、循环渲染例子
<div id="app">
<ul>
<li v-for="(item,index) in fruits" :key="index">
拿数组里的什么 数组
{{index+1+' '+item}} //可放表达式计算,到" "变成string类型,再进行string拼接
</li>
</ul>
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
</tr>
<tr v-for="item in stus":key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>
</table>
</div>
let vm = new Vue({
el: '#app',
data: { //数据模型相当于全局作用域
msg: "hello",
fruits: ['苹果', '橘子', '葡萄'],
stus: [{
id: 1001,
name: 'zhangsan',
age: 12
},
{id: 1002,
name: 'lisi',
age: 13
},
{id: 1003,
name: 'terry',
age: 14
}],
},
methods:{
},
})
2、条件渲染例子
<div id="app">
<div v-if="isLogin">欢迎您</div>
<div v-else>请登录</div>
<!--登录成功就显示-->
<div v-show="isLogin">仅用来测试是否显示-------</div>
</div>
let vm = new Vue({
el: '#app',
data: {
isLogin: false
},
})
五、插值
1、文本、JS表达式
基本渲染:{{}}
2、原始html
<span v-html="rawHtml"></span>
3、事件
<span @click="doSomething"></span>
<span v-on:click="doSomething"></span>
4、属性
<span v-bind:id=" "></span>
v-bind:可简写成:
六、style和class属性:取值是对象/数组
1、style绑定
<div v-bind:style="{color:activeColor,fontSize:fontSize+"px"}">
"[baseStyles,overridingStyles]"
</div>
例子:
<div id="app">
<div :style="styleObj1">Hello</div>
<div :style="{color:currentColor}">World</div>
<div :style="[styleObj1,styleObj2]"> hello world</div>
</div>
new Vue({
el:'#app',
data:{
styleObj1:{
color:'red',
"font-size":'30px',
},
currentColor:'blue',
styleObj2:{
background:'pink',
color:'blue'
}
},
methods:{
}
})
结果:
2、class绑定
<div v-bind:class="{类名:布尔值}">
"[{类名:布尔值},{类名:布尔值}]"
"[isActive?activeClass:",errorClass]"
</div>
例子:
.red {
color: red;
}
.size {
font-size: 20px;
}
<div id="app">
<div class="red" :class="{size:true}">Hello</div>
<div :class="{red:false,size:true}">World</div>
<div :class="[{red:true},{size:false}]">hello World</div>
</div>
new Vue({
el: '#app',
data: {
},
methods: {
}
})
七、事件
1、事件绑定
<button v-on:click="greet">Greet</button>
methods:{
greet:function(event){
if(event){alert(event.target.tagName)} //event是原生dom事件
}
}
2、事件传参
例子:模拟表格产生,添加删除按钮
之前:<button data-id="1001">删除按钮</button>
$(this).attr('data-id');
<div id="app">
<table>
<tr v-for="item in arr" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td> <!--加一个()可以运行,运行时传递实参-->
<button @click="toEdit($event,item)">修改</button> <!--传事件对象$event-->
<button @click="toDelete($event,item)">删除</button> <!--光传item也行-->
</td>
</tr>
</table>
</div>
new Vue({
el:'#app',
data:{
arr:[{
id:1001,
name:'zhangsan',
age:12
},
{
id:1002,
name:'lisi',
age:13
},
{
id:1003,
name:'terry',
age:14
}]
},
methods:{
toEdit(event,item){
console.log(event,item);
},
toDelete(event,item){
console.log(event,item);
}
}
})
3、事件修饰符
以前:event.preventDefault()或event.stopPropation()
Vue——用事件修饰符
<form v-on:submit.prevent="onSubmit"></form>
.prevent 阻止事件默认行为( a标签、button按钮)
.stop 停止事件冒泡
父子元素绑定具有冒泡行为的事件,才有冒泡行为产生
.capture 捕获阶段执行
.self 只当在event.target是当前元素自身才触发,只有当点击outer才会执行
.once 执行一次
.passive 滚动事件的默认行为(即滚动行为)会立即触发 ,一般与scroll连用,能够提升移动端的性能
按键修饰符,一般与keyup事件类型配合使用
.enter(键码值)、.tab、.delete、.esc、.space、.up、.down、.left、.right
.ctrl、.alt、.shift、.meta
鼠标修饰符,mouseup
.left、.right、.middle
例子:
/*当里边比外边大,就会出现滚轮效果*/
.outer {
width: 200px;
height: 200px;
background-color: coral;
overflow: scroll;
}
.inner {
width: 100px;
height: 400px;
background-color: cyan;
margin: 50px;
}
new Vue({
el: "#app",
data: {
msg: '事件修饰符'
},
methods:{
toJump(){
console.log('跳转');
},
outer(e){
//e.target 触发事件的源头元素
//e.currentTarget 当前元素
console.log('outer')
for (let i=0;i<100;i++){
console.log(i);
}
},
inner(e){
//e.stopPropagation();
//console.log('inner',e.target,e.currentTarget);
console.log('inner');
},
keyupHandle() {
console.log('按下某些特殊键');
},
}
})
<div id="app">
<!--前后顺序不同,结果不同,one和prevent-->
<a @click.one.prevent="toJump" href="http://www.baidu.com">百度一下</a>
<!--滚动时会先执行事件处理程序,再产生滚动效果-->
<div class="outer" @scroll.passive="outer">
outer
<div class="inner" @click.capture="inner">
<!--<div class="inner" @click.stop="inner">,不会向外冒泡,只有inner,没有outer-->
<!--点内部子元素outer不触发-->
<!--<div class="outer" @click.self="outer">-->
inner
</div>
</div>
——————————按键修饰符
<!--<input type="text" @keyup.enter="keyupHandle">-->
<input type="text" @keyup.13="keyupHandle">
——————————鼠标修饰符
<!--<input type="text" @mouseup.left="keyupHandle">-->
</div>
八、表单输入绑定
修饰符:增加表单绑定能力
1. lazy
默认情况下,v-model在每次input事件触发后->输入框的值与数据同步改变
可添加lazy修饰符,从而转为在change事件之后进行同步——光标移出再改变
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
2. number
将用户的输入值——>number类型
<input v-model.number="age" type="number">
3. trim
自动过滤用户输入的首尾空白字符,可以给v-model添加trim修饰符
<input v-model.trim="msg">
2. 多行文本框
<textarea v-model="message" placeholder="多行"></textarea>
1. 单行文本框
<input v-model="message" placeholder="单行">
3. 单选按钮,同一个v-model
<input type="radio" value="One" v-model="stu.gender">
<input type="radio" value="Two" v-model="stu.gender">
4. 多选按钮
<input type="checkbox" value="Jack" v-model="stu.hobby">
<input type="checkbox" value="John" v-model="stu.hobby">
5. 下拉框
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
</select>
例子:
<div id="app">
{{stu}}
<br>
<!--简介,多行文本框-->
简介:<textarea v-model="stu.info" cols="30" rows="10"></textarea>
<!--用户名,单行文本框-->
用户名:<input type="text" v-model.trim="stu.username">
<br>
<!--年龄,单行文本框,.number可将数据转为number类型-->
年龄:<input type="text" v-model.number="stu.age">
<br>
<!--性别,单选按钮-->
性别:<input type="radio" value="male" v-model="stu.gender">男
<input type="radio" value="female" v-model="stu.gender">女
<br>
<!--爱好,多选按钮的属性值是数组,因为可能选多个,要放在一个数组里-->
爱好:<input type="checkbox" value="basketball" v-model="stu.hobby">篮球
<input type="checkbox" value="swimming" v-model="stu.hobby">游泳
<input type="checkbox" value="dancing" v-model="stu.hobby">跳舞
<br>
<!--城市下拉框(多选)-->
<select multiple v-model="stu.city">
<option value="shanghai">上海</option>
<option value="beijing">北京</option>
<option value="guangzhou">广州</option>
</select>
<br>
</div>
new Vue({
el:'#app',
data:{
stu:{ //把数据都放在stu对象里
hobby:[] <!--放复选框 选中的值,默认写一个空数组-->
}
},
methods:{}
})
九、计算属性computed——有数据要依赖其他数据的情况
监听属性watch
模板的数据来源:计算属性(除了数据模型data)
需求 a input b input
output标签(H5新增标签):将input的a和b累加后 输出到output标签里
计算属性:computed:{}
当前数据不是确定的,要经常做出改变,这个改变是其他数据改变导致的
有依赖关系的数据(a或b变时,total也变)
基于响应式依赖进行缓存,只在改变时它们才会重新求值
事件监听:watch:{}
当需要在数据变化时——执行异步或开销较大的操作时
浅度监听:只能监听到引用地址是否变化
深度监听:基本数据类型不区分,对象才区分
更改数据类型也能监听到
例子(监听、计算属性):
new Vue({
el: '#app',
data: {
a: 0,
b: 0,
total: 0
},
methods: {},
————//计算属性
computed: {
total(){
return this.a+this.b;
}
}
————//监听属性,a或b发生变化就+一下
watch: {
a(newValue, oldValue) {
this.total = this.a + this.b;
},
b(newValue, oldValue) {
this.total = this.b + this.a;
}
}
})
<div id="app">
a:<input type="text" v-model.number="a">
<br>
+
<br>
b:<input type="text" v-model.number="b">
<br>
=
<br>
<output>{{total}}</output>
</div>
深度监听例子:
new Vue({
el: '#app',
data: {
a: 1,
obj: {
name: 'zhangsan',
age: 12
},
},
methods: {
changeObj() {
//更改内部数据
this.obj.name ='lisi';
/*更改指向
this.obj={
...this.obj, //拿到内部所有属性,放到新的里面
name:'lisi'
}; */
/* let obj2={...obj}
let arr2=[...arr];*/
}
},
watch:{
a(newValue,oldValue){
console.log('a数据发生变化...');
},
/*浅度监听
obj(newValue, oldValue) {
console.log('obj数据发生变化...');
} */
//深度监听
obj:{
handler(newValue, oldValue){
console.log('obj数据发生变化...');
},
deep: true
}
}
})
<div id="app">
{{obj}}
<button @click="changeObj">更改obj</button>
</div>
十、组件
听到day04-1,1:19:26到最后
1、组件注册
1. 全局注册:任何一个Vue实例对应的模板中都可使用该组件
Vue.component('my-component-name',component)
在html使用, 创建组件名
2. 局部注册:只能在当前实例对应的模板中使用
new Vue({
data:{},
methods:{},
components:{ 'component-a': ComponentA, 'component-b': ComponentB }
})
例子:
//1.创建组件
let myCom={
data(){
return{
comMsg: "组件数据"
}
},
template:`
<div>
<span>{{comMsg}}</span>
<button @click=" comMsg='新数据' ">更改数据模型中的数据</button>
</div>
`
};
//2.组件注册(全局)
Vue.component('my-com',myCom);
new Vue({
data:{
},
methods:{}
})
<div id="app">
//3.组件使用
<my-com></my-com>
<my-com></my-com>
</div>
结果:点击按钮后,“组件数据”——>新数据
例子:
<div id="app">
<my-com-b></my-com-b>
----------
<my-com-a></my-com-a>
</div>
let myComA={
template:`
<div>A组件</div>
`
}
let myComB={
components:{
"my-com-a":myComA
},
//在B组件中使用A组件
template:`
<div>B组件
<my-com-a></my-com-a>
</div>
`
}
//全局注册A、B组件
//Vue.component('my-com-a',myComA);
Vue.component('my-com-b',myComB);
new Vue({
el:'#app',
components:{
"my-com-a":myComA
},
data:{
},
methods:{}
})
结果:B组件
A组件
----------
A组件
3、组件通信
1.父组件 属性绑定——>子组件
<my-com title="" :msg="msg" attr-a=""></my-com>
子组件可定义数据类型:<my-com title="hello" :a="1" :obj="{name:'zhangsan'}" :flag="true">
</mycom>
2.子组件接收并处理数据
{
props:['title','msg','attrA'],
或 子组件对接收的数据有要求
props:{
title:String,
obj:Object
}
template:
}
——子组件发射自定义数据,父组件接收
子组件发射时机:1.点击按钮手动发送
2.监听子组件内数据变化,发射
监听子组件comMsg的变化,发射this.$emit(' ',this.comMsg)
父组件的事件处理程序调用,可更改父组件数据模型中的数据,并同步反映到父组件视图中
子组件模板内可使用自己内部/props的数据,不能直接使用父组件的数据(通过props)
例子:
<div id="app">
<my-com @my-event="myEventHandle" :title="msg" static-attr="父组件给子组件的静态数据"></my-com>
</div>
//1.创建子组件
let myCom={
props:["title","staticAttr"],
data(){
return{
comMsg:"子组件数据"
}
},
template:`
<div>
<span>子组件内部数据:{{comMsg}}</span>
<br/>
<span>父组件数据:{{title}}--{{staticAttr}}</span>
<button @click="comMsg='新数据'">更改数据模型中的数据</button>
<button @click="toEmit">发射数据</button>
</div>
`,
methods:{
toEmit(){
this.$emit('my-event',this.comMsg,100);
//自定义事件名, 实参(发射的数据)
}
}
};
//2.(全局)注册子组件
Vue.component('my-com',myCom);
//创建Vue实例——父组件
new Vue({
el:'#app',
data:{
msg:'hello'
},
methods:{
//my-event的事件处理程序,父组件接收
myEventHandle(a,b){
console.log(a,b,'----');
}
}
})
prop例子:
四种校验:数据类型type、默认值default、必填项required
自定义验证函数:如父组件给子组件传递大于20的值
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA:Number,
// 多个可能的类型
propB:[String, Number],
// 必填的字符串
propC:{ type: String, required: true },
// 带有默认值的数字
propD:{ type: Number, default: 100 }
<div id="app">
<my-a :msg="msg" sub-msg="父给子" :is-active="true" :age="80":stus="[1,2,3]"></my-a>
</div>
//子组件
let myA={
//接收父组件的数据
props:{
msg:String,
subMsg:String,
stus:{
type:Array,
default(){ //Object/Array,需写工厂函数返回默认值
return[6,7,8];
}
},
isActive:[String,Boolean],
name:{
type:String,
default:"lisi"
},
/*数据类型type、默认值default、必填项required
自定义验证函数:如父组件给子组件传递大于20的值*/
age:{
type:Number,
//校验器 h5新增表单属性 novalidate formtarget formaction formmethod formenctype formnovalidate
validator(value){ //实参是将来父组件给子组件传递的数据
/*if(value>50){
return true;
}else{
return false;
},和下边一个意思*/
return value>50;
}
}
},
template:`
<div>
{{subMsg}}
{{stus}}
{{isActive}}
{{name}}
{{age}}
{{msg}}
</div>
`
};
//注册子组件
Vue.component('my-a',myA);
let vm=new Vue({
el:'#app',
data:{
msg:'hello'
},
methods:{}
})
4、elementui组件库
https://element.eleme.cn/#/zh-CN
6、插槽(操作的是模板)——可重用性
个性化组件——子组件传递的内容不一样,显示的不一样
调用时传递标签内容,传到模板的slot里
插槽决定将所携带的内容,插入到指定某个位置,从而使模板分块
父组件控制——显不显示、怎样显示
子组件控制——在哪里显示
1、普通(默认)插槽:默认名字是default
2、具名插槽
template:`
<div>
<slot name="default"></slot>
<slot name="header"></slot>
</div>
`
<my-a>
<template v-slot:default>hello</template>
<template v-slot:header>world</template> //包裹要填充的内容
</my-a>
3、作用域插槽:子组件数据想用slot的模板——一般在插件里用
data(){return {msg:''}}
template:`
<div>
<slot :title="msg" name="default"></slot>
<slot name="header"></slot>
</div>
`
<my-a>
<template v-slot:default="scope">hello{{scope.title}}</template>
<template v-slot:header>hello</template>
</my-a>
例子:
<div id="app">
<my-com>Hello</my-com>
<my-com>World</my-com>
<my-com></my-com>
<my-com> <!--具名插槽填充-->
<!--在父组件环境内-->
<template #default>
<div>
<!--模板中如何使用父/子组件的数据-->
123
456
{{msg}} <!--可访问父组件中的数据-->
</div>
</template>
<!--<template #slot2="scope">
scope:插槽作用域对象{插槽声明时传递的属性},{sub:'子组件的数据'}-->
<template #slot2="{sub,test}">
<div>
{{sub}}
{{test}}
<!--{{scope}}
{{scope.sub}}-->
<h1>标题</h1>
</div>
</template>
</my-com>
</div>
/*全局注册子组件,模板内有1个普通插槽,1个具名插槽
在子组件环境内*/
Vue.component("my-com",{
data(){
return{
subMsg:'子组件的数据'
}
},
template:`
<div>
day04
<button>
<slot :a="subMsg" name="default">默认内容1,{{subMsg}}</slot>
</button>
<div>
<slot :sub="subMsg" name="slot2" :test="1">默认内容2</slot>
</div>
--------------------------------------------------------------------------------
</div>
`
})
//父组件
new Vue({
el:'#app',
data:{
msg:'hello'
},
methods:{}
})
8、综合例子
day04-2整个
<div id="app">
<my-table :data="stus">
<template #header>
<th>编号</th>
<th>名称</th>
<th>年龄</th>
</template>
<template #body="{item}">
<th>{{item.id}}</th>
<th>{{item.name}}</th>
<th>{{item.age}}</th>
</template>
</my-table>
<my-table :data="teachers">
<template #header>
<th>编号</th>
<th>名称</th>
<th>薪资</th>
<th>性别</th>
</template>
<template #body="{item}">
<th>{{item.id}}</th>
<th>{{item.name}}</th>
<th>{{item.salary}}</th>
<th>{{item.gender}}</th>
</template>
</my-table>
<my-table :data="courses">
<template #header>
<th>编号</th>
<th>名称</th>
<th>描述</th>
</template>
<template #body="{item}">
<th>{{item.id}}</th>
<th>{{item.name}}</th>
<th>{{item.desc}}</th>
</template>
</my-table>
</div>
//全局注册组件
Vue.component('my-table',{
data() { return {} },
props: ['data'],
template: `
<table>
<thead>
<tr>
<slot name="header"></slot>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in data" :key="index">
<slot :item="item" name="body"></slot>
</tr>
</tbody>
</table>
`
})
//父组件,el,data(msg,stus三个,teachers四个,courses两个),methods
new Vue({
el: '#app',
data: {
msg: 'hello',
stus: [{
id: 1001,
name: 'zhangsan',
age: 12
},
{id: 1002,
name: 'zhangsan2',
age: 13
},
{
id: 1003,
name: 'zhangsan3',
age: 14
}],
teachers: [{
id: 101,
name: 'terry',
salary: 10000,
gender: 'male'
},
{
id: 102,
name: 'terry2',
salary: 100001,
gender: 'female'
},
{
id: 103,
name: 'terry3',
salary: 1000011,
gender: 'male'
},
{
id: 104,
name: 'terry4',
salary: 10000111,
gender: 'male'
}],
courses: [{
id: 1,
name: 'HTML',
desc: '超文本'
},
{id: 2,
name: 'CSS',
desc: '层叠样式表'
}]
},
methods: {}
})
9、动态组件(component标签+is属性)
企业级开发的底层 day04-3——>23.17
十一、可复用技术
1、混入mixin(对象)
混入规则:——冲突时,以组件优先
同名钩子函数混合为一个数组,钩子函数都执行,混入钩子函数——>组件钩子函数
值为对象的属性methods,computed,components,filters,directives——>合并对象
data中有引用数据类型——>递归合并(深合并)
深合并:name,age+gender=name,age,gender
name+age,name=组件中的name+混入对象的age
var mixin={
data:function(){return {info:"this is a mixin"}},
created(){console.log("this is mixin created");},
methods:{foo(){return "this is mixin foo";}}
}
new Vue({
el:"#app",
mixins:[mixin], //将上方的mixin对象混入当前Vue实例内
data:{msg:"hello"},
created(){console.log("this is vue created");},
methods:{foo(){return "this is vue foo";}}
})
Vue.mixin(mixin);
全局混入:会影响每个之后创建的Vue实例
谨慎使用,推荐作为插件发布
Vue.mixin({
created:function(){}
})
全局混入例子
<div id="app">
<my-a></my-a>
<!--混入、Vue实例
混入、子组件
混入created
created...
混入created
子组件created-->
</div>
//混入(对象),可放声明组件的任何东西
let mixin={
data(){
return{
mixinData:'混入的数据',
obj:{name:'zhangsan',gender:'男' }
}
},
created(){
console.log('混入的created');
},
methods:{
mixinMethod(){
console.log('mixinMethod方法');
},
test(){
console.log('test 混入');
}
}
//原型方法 vm.$destory
};
//子组件
let myA={
template: `
<div>
A组件内容
</div>
`,
created(){
console.log('A组件created');
}
};
//全局混入
Vue.mixin(mixin);
//全局注册子组件
Vue.component('my-a',myA);
let vm=new Vue({
el:'#app',
/*局部混入
mixins:[mixin],*/
data:{
msg:'hello',
obj:{age:12,name:'lisi'}
},
created(){
console.log('created...');
},
methods:{
test(){
console.log("test");
}
}
})
结果检测:通过控制台
2、自定义指令(对象)
v-开头的是指令,v-bind指令
directive:指令允许对普通DOM元素进行底层操作,可全局、局部注册
1. 全局注册
Vue.directive("focus",{
//当被绑定的元素插入到 DOM 中时„„
inserted: function (el) {
// 聚焦元素,el为DOM中的Element元素
el.focus()
}
})
<input v-focus>
2. 局部注册
directives: {
focus: {
inserted: function (el) { el.focus() }
}
}
指令的生命周期:bind(el,binding,vnode,oldNode){}
bind:只需要调用一次,在绑定的时候调用进行初始化设置
inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)
update:VNode更新时调用,但可能发生在其子VNode更新之前
componentUpdated:组件的VNode及其子VNode全部更新后调用
unbind:指令与元素解绑时调用,只调用一次
参数:el:dom元素
binding:对象,指令的详细参数
vnode:Vue编译生成的虚拟节点
oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用
自定义指令例子:day04-3,1.12.20到4-4,16.54
3、过滤器filter(格式化)函数
使用:{{msg|过滤器}}
<div v-bind:title="msg|过滤器"></div>
应该被添加在JS表达式的尾部,由“管道”符号|指示
new Date().getTime() //获取当前时间的时间戳
处理成年月日时分秒
{{date|filter}}
{{哪个变量|调用哪个过滤器}}
1、以前的方式
let arr=[{
name:'',
time:1600676292941
},
{
name:'',
time:1600676613869
},
{
name:'',
time:1600676624202
}];
arr.forEach((item)=>{
item.time=parse(item.time);
})
console.log(arr);
function parse(time){
let date=new Date(time); //将time——>时间对象
return date.toLocaleDateString()+' '+date.toLocaleTimeString();
本地日期字符串(年月日) 本地时间字符串(时分秒)
2、过滤器方式
时间过滤器一般用全局注册
全局注册:过滤器名字,过滤器函数
Vue.filter('过滤器名字', function(date){
return date?moment(date).format('YYYY-MM-DD HH:mm'):'';
})
{{ birthday |过滤器名称}}
局部注册:
filters:{
过滤器名称:function(date){
return date?moment(date).format('YYYY-MM-DD HH:mm'):'';
}
}
{{ birthday |过滤器名称}}
<div id="app">
{{msg|parseUpper}}
-----
<div :title="msg|parseUpper" v-for="item in arr" :key="item.time">
<span>item.name:{{item.name}}</span>
<span>过滤器:{{item.time|parseTime}}</span>
</div>
</div>
//全局注册过滤器 {{item.time|parseTime}}
Vue.filter('parseTime',(date)=>{
//date就是item.time 2020-09-09 09:09:09 2020/09/09 09:09:09
return new Date(date).toLocaleString();
});
/*全局注册大写过滤器
Vue.filter('parseUpper',(date)=>{
return date.toUpperCase();
});*/
new Vue({
el:'#app',
//局部注册过滤器
filters:{
parseUpper(date){
return date.toUpperCase();
}
},
data:{
msg:'hello',
arr:[{
name:'1',
time:1600676292941
},
{
name:'2',
time:1600676613869
},
{
name:'3',
time:1600676624202
}]
},
methods:{}
})
4、渲染函数(使用频率不高)
带$slots调用者是Vue实例
模板产生:createElement或jsx
1)render(创建Vnode)
JS的原生DOM操作创建div节点
document.createElement("div');
render渲染函数:灵活、效率高
render(createElement){
createElement(标签名称,{配置信息(可不写)},childVnode)
子虚拟节点,一般为数组
class、style、attrs、props、domProps、On、nativeOn
2)jsx(在js里写html代码),可分开使用,但依赖render函数渲染
如果写了很多render函数,可能会觉得其语法相对比较琐
在Vue中使用JSX语法,可让我们回到更接近于模板的语法上
jsx的测试需要放到脚手架中进行
new Vue({
el: '#demo',
render: function (h) {
return (<div> <span>Hello</span> world! </div>)
}
})
render(){
return (<div>
hello{this.msg}
<ul>
{let arr=['苹果','香蕉'],
arr.map((item)=>{
return <li>{item}</li>
})
}
</ul>
</div>
}
例子:
<div id="app">
<my-a :level="1">
<template #default>default</template>
<template #header>header</template>
</my-a>
</div>
//组件myA声明
let myA={
props:{
level:{
type:Number,
required:true
}
},
/*render(createElement){
//jsx
return (<div>hello{this.msg}</div>) //{写JS代码}
}*/
//<h1><slot name="default"></slot></h1>
render(createElement){
return createElement(
'h'+this.level,
this.$slots.header //想用自定义模板中的内容
/*{
default:default模板,
header: header模板,
}*/
);
}
/*render(createElement){
return createElement('div',{ //在控制台显示div内放文本
attrs:{
id:'one',
title:'test'
}
},'文本')
}*/
/*template: `
<div>
Hello
</div>
` */
};
//组件myA注册
Vue.component('my-a',myA);
new Vue({
el:'#app',
data:{
msg:'hello'
},
methods:{}
})
5、插件(对象)plugin(Vue构造器,配置对象可选)
封装了一系列功能
通常用来为Vue添加全局功能,有install方法
插件的使用在new Vue(创建)之前,因为插件里可能有对Vue构造器和原型对象的操作
new Vue({
MyPlugin.install=function(构造器Vue, 选项对象options){
Vue.myGlobalMethod=function(){//逻辑...} //添加全局方法
Vue.directive('my-directive',{}) //添加全局资源
Vue.mixin({ reated:function(){//逻辑...} ..}) //注入组件选项
Vue.prototype.$myMethod=function(methodOptions){//逻辑...} //添加实例方法
}
}
使用:在new Vue之前调用Vue.use()静态方法Vue.use(MyPlugin)
相当于调用插件本身的install方法
例子:
<div id="app">
{{msg}}、{{msg|myFilter}}
</div>
/*声明插件对象
以后引用别人的插件:import导入*/
let myPlugin={
//install方法在Vue.use(myPlugin)会运行
install(Vue,options){ //一定要有install方法
//提供静态属性或方法
Vue.isVue=()=>{ }
//提供原型属性或方法
Vue.prototype.$sayVue=()=>{ }
//全局注册(组件、过滤器、指令、混入)
Vue.filter('myFilter',(data)=>{
return data.toUpperCase()
})
}
};
//console.log(Vue.isVue);
//使用插件(在new Vue前使用),使用时实际是调用插件对象的install方法
Vue.use(myPlugin);
/*console.log(Vue.isVue);
Object.prototype.say=()=>{ }
let obj=new Object()
obj.say();*/
let vm=new Vue({
el:'#app',
data:{
msg:'hello'
},
methods:{}
})
//console.log(vm.$sayVue);
十二、核心插件Vue-router
安装:cdn导入在线资源
<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>
2、基本应用
1. 组件定义
let foo={template:`<div>this is foo</div>`};
let bar={template:`<div>this is bar</div>`};
2. 创建路由器对象
var router=new VueRouter({
routes:[{path:"/foo",component:foo},{path:"/bar",component:bar}]
})
3. 组件注册
new Vue({
el:"#app",
router
})
4. 模板内容
<router-link to=“/foo”>跳转到foo</router-link>
<router-link to="/bar">跳转到bar</router-link>
<div id="app">
<router-view></router-view>
</div>
例子:
<div id="app">
<!--4.使用路由-->
<div>
<router-link to="/a">去A路由</router-link>
<a href="#/a">a标签路由去A</a>
<router-link to="/b">去B路由</router-link>
</div>
<div>
<!--组件显示的位置-->
<router-view></router-view>
</div>
</div>
//声明组件
let myA={
template: `
<div>A组件</div>
`
};
let myB={
template: `
<div>B组件</div>
`
};
3、路由重定向——刚一进入页面就有内容
path redirect
path component
4、重命名
let routes=[{
path:'/',
/*component:myA
路由重定向
redirect:'/a'
redirect:{name:'aRoute'}*/
},
{path:'/a',
component:myA,
//路由名称
name:'aRoute',
//重命名,a和aa都可跳到a组件
alias:'/aa'
},
{path:'/b',
component: myB,
//路由名称
name:'bRoute'
}
];
6、动态路由声明
User组件(同一个模板,不同的用户)
/user/:1001 User组件加载1001用户的数据
/user/:1002 User组件加载1002用户的数据
{
path:'/user/:id', //和普通路由的区别在path
name:'',
component:User,
}
接收 动态路由参数:this.$route.params.id
例子:
<div id="app">
<div>
<router-link to="/user/id/1/username/zhangsan">去A路由</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
//声明组件
let myA={
data(){
return{
id:null,
username:''
}
},
template: `
<div>A组件{{id}}--{{username}}</div>
`,
watch:{
msg(newValue,oldValue){ },
$route(新路由to,旧路由from){
this.id=this.$route.params.id; //将路由对象的...复制到数据模型中
this.username=this.$route.params.username;
}
}*/
//监听路由变化——路由守卫(写在了myA组件内,在myA组件内路由变化才会触发路由守卫)
beforeRouteUpdate(to,from,next){ //继续执行下一步,无next:只更新路由 不跳转组件
console.log(to.params);
this.id=to.params.id;
this.username=to.params.username;
//继续 next();
//阻断 next(false); //(一般不这么用)可控制不跳转路由,页面变了但路由没变
}
};
let myB={
template:`
<div>B组件</div>
`
};
//1.定义路由对象数组
let routes=[{
//动态路由
// /user/id/1/username/zhangsan
// /user/id/2/username/lisi
path:'/user/id/:id/username/:username',
component:myA
}];
//2.创建路由器对象
let router=new VueRouter({
routes:routes
})
//3.注册路由器对象
new Vue({
el:'#app',
router:router,
components:{
'my-a':myA,
'my-b':myB
},
data:{
msg:'hello'
},
methods:{}
})
7、路由守卫(用的比较少,动态路由会用到路由守卫)
1.全局前置守卫:router.beforeEach((to,from,next)=>{})
全局后置守卫:router.afterEach((to,from)=>{})
2.路由独享守卫
{ path:'',
beforeEnter(to,from,next){}
}
3.组件内守卫:监听组件内路由变化
beforeRouteEnter(to,from,next){
console.log(this,'beforeRouteEnter'); //this-->window
next();
},
beforeRouteUpdate(to,from,next){
console.log(this,'beforeRouteUpdate'); //this-->组件实例
next();
},
beforeRouteLeave(to,from,next){
console.log(this,'beforeRouteLeave'); //this-->组件实例
next();
}
{ sayName(){this-->obj},
sayName:()=>{this-->vue实例}
}
obj.sayName();
例子
<div id="app">
<!--4.使用路由-->
<div>
<router-link to="/a/1">去A路由</router-link>
<router-link to="/b">去B路由</router-link>
<a href="#/a/2">a标签路由去A</a>
</div>
<div>
<!--路由组件显示的位置-->
<router-view></router-view>
</div>
</div>
//声明组件
let myA={
template:`
<div>A组件</div>
`,
//2.3设置 组件内守卫
beforeRouteEnter(to,from,next){
//this-->window
console.log(this,"beforeRouteEnter");
next();
},
beforeRouteUpdate(to,from,next){
//this-->组件实例
console.log(this,'beforeRouteUpdate');
next();
},
beforeRouteLeave(to,from,next){
//this-->组件实例
console.log(this,'beforeRouteLeave');
next();
}
};
let myB={
template:`
<div>B组件</div>
`
};
//1.定义路由对象数组 route路由对象 router路由器对象 routes路由对象组
let routes=[{
path:'/a/:id',
component:myA
},
{ path:'/b',
component:myB,
//2.2设置 路由独享守卫
/*beforeEnter(to,from,next){
console.log(to,from,next,'beforeEnter');
next();
}*/
}];
//2.创建路由器对象
let router=new VueRouter({
routes:routes
})
//2.1设置 全局路由前置/后置守卫
/*router.beforeEach((to,from,next)=>{
console.log(to,from,next,'beforeEach----');
next();
})
router.afterEach((to,from)=>{
console.log(to,from,'afterEach----');
if(to.path=='/a'){ //跳转到a路由做一些事情
console.log('添加其他处理');
}
})*/
//3.注册路由器对象
new Vue({
el:'#app',
router:router,
components:{
'my-a':myA,
'my-b':myB
},
data:{
msg:'hello'
},
methods:{},
})
8、路由嵌套
day6-1, 27.23到53.05
//课程管理
{ path:"/course",
component:Course,
children:[{ path:'grade', //孩子可不写父路由的路径
component:Grade},
{ path:'plan',
component:Plan}
]
}
//成绩表
{ plan:"/course/grade", //二级路由
component:Grade
},
//排课表
{ path:"/course/plan",
component:Plan
}
例子:
<div id="app">
<div style="float:left;">
<router-link to="/student">学生管理</router-link>
<br>
<router-link to="/course">课程管理</router-link>
<br>
<router-link to="/course/grade">成绩</router-link>
<router-link to="/course/plan">排课</router-link>
</div>
<div style="float:right;">
<router-view></router-view>
</div>
</div>
let Student={
template:`<div>
学生管理
</div>`
};
/*<div>
<router-link to="/course/grade">成绩</router-link>
<router-link to="/course/plan">排课</router-link>
</div>*/
let Course={
template:`<div>课程管理模块
<div>
<router-view></router-view>
</div>
</div>`
};
let CourseGrade={
template:`<div>
成绩表
</div>`
};
let CoursePlan={
template:`<div>
排课表
</div>`
};
let routes=[{
path:'/student',
component:Student
},
{ path:'/course',
component:Course,
redirect:'/course/grade',
children:[{
path:'grade',
component: CourseGrade
},
{path: 'plan',
component: CoursePlan
}
]
}/*{
path:'/course/grade',
component:CourseGrade
}*/]
let router=new VueRouter({
routes:routes
})
new Vue({
el:'#app',
router,
data:{
msg:'hello'
},
methods:{}
})
9、导航(跳转路由)
day6-1,59.13到1.29.42
带不带参
push({
path:'/a?查询字符串'
})
push({
path:'/a',
query:{}
})
例子:
<div id="app">
<!--4.使用路由-->
<div>
<router-link to="/a">去A路由</router-link>
<router-link to="/b">去B路由</router-link>
<a href="#/a">a标签路由去A</a>
<button @click="$router.push('/a')">跳转到A1</button>
<button @click="$router.push('a')">跳转到A2</button>
<button @click="toPath">跳转到A3</button>
<button @click="toPath2">跳转到A4带参</button>
</div>
<div>
<!--路由组件显示的位置-->
<router-view></router-view>
</div>
</div>
//声明组件
let myA={
template:`
<div>A组件</div>
`,
//接受路由参数
created(){
console.log(this.$route);
}
};
let myB={
template:`
<div>B组件</div>
`
};
//1.定义路由对象数组 route路由对象 router路由器对象 routes路由对象组
let routes=[{
path:'/a',
name:'mya',
component:myA
},
{
path:'/b',
name:'myb',
component:myB
}];
// 2.创建路由器对象
let router=new VueRouter({
routes:routes,
//路由模式 哈希路由
mode:'hash'
//历史记录路由,浏览器路由browser
//mode:'history'
})
//3.注册路由器对象
new Vue({
el:'#app',
router:router,
components:{
'my-a':myA,
'my-b':myB
},
data:{
msg:'hello'
},
methods:{
//路由跳转
toPath(){
this.$router.push('a');
//this.$router.push({ path:'/a'})
//this.$router.push({ name:'mya'})
//路由记录、历史记录跳转
//this.$router.go(1)
//替换路由
//this.$router.replace('/a');
},
//带参跳转
toPath2(){
this.$router.push({
name:'mya',
//参数带在查询字符串,刷新页面不会消失
query:{
id:1
},
//参数是一次性携带,刷新页面会消失
params:{
name:'zhangsan'
}
})
/*this.$router.push({
//有效,带在query上
//path:'/a?id=1001',
path:'/a',
//带参
//有效
query:{
id:1
},
//params无效
}) */
},
}
})
十三、Vuex
在火狐浏览器(扩展和主题),搜索Vue devtools,添加到火狐中(扩展)
然后在控制面板中就有了
十四、axios
引入:bootcdn、cdnjs(涵盖范围少)
//Vue-router和Vuex在企业级开发中——模块化
用npm或cnpm安装axios:$npm install axios --save
github资源
十五、JSX可作为渲染函数返回的模板
render(createElement){
return(<div>hello</div>)
}
十六、脚手架Vue-cli
全局安装Vue-cli工具,安装该工具后就可使用Vue命令了
全局安装:cnpm install -g @Vue/cli
创建项目:Vue create my-project