<script src="../js/vue.js" type="text/javascript">script>
head>
<body>
<div id="root">
<h1>hello,I am {{name}}h1>
<h1>我的年龄是,{{age}}h1>
div>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//创建Vue示例
new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data: { //用于存储数据,数据供el指定的容器使用
name: '靓仔',
age: '21'
}
})
script>
body>
插值语法:
功能:用于解析标签体内容。
写法: {{xx}}. xxx是js表达式,且可以直接读取到data中的所有属性。
指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)。
举例: v-bind:href=“xx” 或简写为 :href=“xxx”, xxx同样要写js表达式,
且可以直接读取到data中的所有属性。
备注: Vue中有很多的指令,且形式都是: v-???, 此处我们只是拿v-bind举个例子
v-bind //v-bind:href="url" 使用v-bind:属性="..." 可以将所有的属性中的内容变成表达式
可以简写为 ':'
<h1>指令语法h1>
<a v-bind:href="url">百度a>
<a :href="baidu.url1">百度a>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//创建Vue示例
new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data: { //用于存储数据,数据供el指定的容器使用
url: 'https://www.baidu.com',
<!--属性名重复时可使用层级结构分开,调用时使用外层的baidu.属性(url)-->
baidu:{
url: 'https://www.baidu.com'
}
}
})
script>
单向绑定和双向绑定:
<input type="text" v-bind:value="url">百度input>
<input type="text" v-model:value="url">百度input>
v-model:value="url" 可以简写为v-model="url" 因为它默认就是获取value值
el 的两种写法:
//创建Vue示例
const x = new Vue({
//第一种 el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data: { //用于存储数据,数据供el指定的容器使用
name: '靓仔',
age: '21'
}
})
//第二种
x.$mount('#root');
data 的两种写法:
//第一种 (对象式)
data: { //用于存储数据,数据供el指定的容器使用
name: '靓仔',
age: '21'
}
//第二种 (函数式)
data: function () {
return {
name: 'xxx'
}
}
<a :href="url1">{{name}}a> =========》 V view视图
div>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//创建Vue示例
new Vue({ =========》 VM : 实例
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data: { //用于存储数据,数据供el指定的容器使用
name: '靓仔',
age: '21' =========》M :model
}
})
script>
示例:
<script type="text/javascript">
var number = 20;
let person = {
name: 'zhangsan',
sex: '男',
}
Object.defineProperty(person, 'age', {
value: 20,
enumerable: true, //控制属性是否可以枚举,默认值是false
writable: true, //控制属性是否可以被修改,默认值是false
configurable: true, //控制属性是否可以被删除,默认值是false
//当有人读取person的age属性时,get函数(getter)就会被调用,且返 回值就是age
get: function () {
return number;
},
set(value) {
console.log("修改值为:",value);
number = value;
}
})
console.log(Object.keys(person))
script>
在控制台Vue中输出data的值: vm._data.属性名
通过vm对象来代理data对象中属性的操作(读/写)
Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过0bject . defineProperty( )把data对象中所有属性添加到vm上
为每一 个添加到vm 上的属性, 都指定一一个getter/setter 。
在getter/setter内部去操作(读/写) data中对应的属性。
语法:
v-on:click=“showInfo” 简写 @click=“showInfo2(666)”
示例:
<div id="root">
<button v-on:click="showInfo">点我欢迎(不传自定义参)button>
<button @click="showInfo2(666)">点我欢迎(传自定义参)button>
div>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//创建Vue示例
const vm = new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data() {
return {
name: 'xxx'
}
},
methods: {
showInfo(event) { //event.target拿到该事件的目标 (这里就是button标签本身).innerText 获取文本
console.log(event.target.innerText)
alert("你好光哥");
},
showInfo2(number) {
console.log(number)
alert("你好光哥" + number);
}
}
})
script>
Vue中的事件修饰符:
<a href="hppts://www.baidu.com" @click.prevent="aclick">不跳转到百度a>
<button v-on:click="showInfo">点我欢迎(不传自定义参)button>
<button @click.stop="showInfo">点我欢迎(传自定义参)button>
once: 事件只触发一次(常用) ,调用过后就失效
capture:使用事件的捕获模式;
self:只有event . target是当前操作的元素是才触发事件;
passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
键名 | 别名 |
---|---|
回车 | enter |
删除 | delete(捕获删除和退格键) |
退出 | esc |
空格 | space |
换行 | tab(特殊,必须配合keydown使用) |
上 | up |
下 | down |
左 | left |
右 | right |
Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊) : ctr1、alt、shift、 meta
(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2).配合keydown使用:正常触发事件。
也可以使用keyCode去指定具体的按键(不推荐)
Vue . config. keyCodes.自定义键名=键码,可以去定制按键别名
示例:
<div id="root">
<input type="text" placeholder="按下回车提示" @keyup.enter="showInfo">
div>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//创建Vue示例
const vm = new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data() {
return {
name: 'xxx'
}
},
methods: {
showInfo(e) {
console.log(e.Key,e.keyCode) //获取按下的键和它的编码号
console.log(e.target.value); //获取标签的value值
}
}
})
// x.$mount('#root'); //和el: "#root"同效果
script>
姓:<input type="text" v-model="firstName" />
<br />
名:<input type="text" v-model="lastName" />
<br />
全名
<span>{{fullName()}}span>
const vm = new Vue({
methods: {
fullName() {
return this.firstName + '-' + this.lastName
}
}
})
const vm = new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
computed: {
fullName: {
get() {
return this.firstName + '-' + this.lastName
},
set(value) {
const arr = value.slice('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
computed: {
fullName() { //等同于fullName: {get() {}
return this.firstName + '-' + this.lastName
}
}
<button @click="next">点我切换button>
<button @click="isName=!isName">点我切换(简写,当操作简单时使用)button>
methods: {
next() {
return this.isName = !this.isName;
}
},
computed: {
fullName() {
return this.firstName + '-' + this.lastName
},
name() {
return this.isName ? "靓仔" : "帅哥"
}
}
使用监视属性:
//第一种
watch:{
//要监视的对象
isName:{
immediate:true, //初始化时调用一下headler
handler(newValue,oldValue){
console.log("值被修改前"+newValue+"值被修改后"+oldValue)
}
}
}
//('要监视的属性',{ 配置项 })
vm.$watch('isName',{
immediate: true, //初始化时调用一下headler
handler(newValue, oldValue) {
console.log("值被修改前" + newValue + "值被修改后" + oldValue)
}
})
<h1>{{numbers.a}}h1>
<button @click="numbers.a++">点我给a++button>
data() {
return {
isName: true,
numbers: {
a: 1,
b: 1
}
}
},
watch: {
//监视多层结构的某个属性
'numbers.a': {
handler() {
console.log("a改变了")
}
}
//监视多层结构的全部属性
numbers: {
deep: true,//开启监视多层结构的全部属性
handler() {
console.log("a改变了")
}
}
}
computed和Iwatch之间的区别:
computed能完成的功能,watch 都可以完成。
watch能完成的功能,computed不一 定 能完成,例如: watch可以进行异步操作。
两个重要的小原则:
所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,
这样this的指向才是Vm或组件实例对象。
1.class样式
写法:class=“xxx” xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
对象写法适用于:要绑定多个样式,个数不确定,名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
<div class="defalut" :class="back" @click="style">{{name}}div>
methods: {
style: function () {
let arr = ['red', 'green', 'black']
let number = Math.floor(Math.random() * 3);
this.back = arr[number];
}
}
<div class="defalut" :class="arrClass" @click="style">{{name}}div>
data() {
return {
name: '杜兰特',
back: 'red',
arrClass:['red', 'green', 'black']
}
},
<div class="defalut" :class="objClass" @click="style">{{name}}div>
data中:
objClass: {
red: false,
green: false
}
style样式
:style=“{fontSize: xx}” 其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象.
显示或不显示:
<div v-show="false">世界上最好的运动员--凯文.{{name}}div>
<div v-if="1===2">世界上最好的运动员--凯文.{{name}}div>
<div v-else-if="1===1">历史第一凯文.{{name}}div>
<template v-if="1===1">
<h1>aaah1>
<h1>bbbh1>
<h1>ccch1>
template>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
li>
<li v-for="(val,key) in audi" :key="key">
{{key}}-{{val}}
li>
ul>
const vm = new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data() {
return {
persons: [
{id: '001', name: '张一', age: 18},
{id: '002', name: '张二', age: 19},
{id: '003', name: '张三', age: 20}
],
audi: {
name: '奥迪rs7',
price: '200w',
color: 'red'
}
}
},
})
key的作用与原理
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据[新数据]生成[新的虚拟DOM] ,
随后Vue进行[新虚拟DOM]与[旧虚拟DOM]的差异比较,比较规则如下:
(1) .旧虚拟DOM中找到了与新虚拟DOM相同的key
①.若虚拟DOM中内容没变,直接使用之前的真实DOM!
②.若虛拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2) .旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
示例:
<input type="text" placeholder="请输入人名:" v-model="choice">
<ul>
<li v-for="(p,index) in filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
const vm = new Vue({
el: "#root", //指定当前Vue实例为那个容器服务,值通常为css选择器字符串
data() {
return {
choice: '',
persons: [
{id: '001', name: '周冬雨', age: 18, sex: '女'},
{id: '002', name: '马冬梅', age: 19, sex: '女'},
{id: '003', name: '周杰伦', age: 20, sex: '男'},
{id: '004', name: '温兆伦', age: 21, sex: '男'}
],
filPersons: []
}
},
//计算属性实现
computed: {
filPersons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.choice) !== -1;
})
}
}
//监视实现
watch: {
choice: {
immediate: true, //运行完直接调用,此时val是空值 数组里包含有空串 所以查询全部
handler(val) {
//过滤原有的数组,判断里面是否包含输入的值,如果有将其返回给新的数组,用于展示
this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
<button @click="sortType=2">年龄升序button>
<button @click="sortType=1">年龄降序button>
<button @click="sortType=0">原顺序button>
data() {
return {
sortType: 0, //0原顺序 1降序 2升序
choice: '',//输入的关键字
persons: [
{id: '001', name: '周冬雨', age: 30, sex: '女'},
{id: '002', name: '马冬梅', age: 19, sex: '女'},
{id: '003', name: '周杰伦', age: 25, sex: '男'},
{id: '004', name: '温兆伦', age: 20, sex: '男'}
],
// filPersons: [] 用watch实现使用
}
},
computed: {
filPersons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.choice) !== -1;
})
if (this.sortType) { //不是执行原顺序的操作,才进行排序
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age :p1.age - p2.age
})
}
return arr;
}
}
12.2 监视数据
vue.set() 方法:
语法: Vue.set(往哪个对象上添加,要添加的属性名,值)
在Vue开发者工具中给某个对象添加一个响应式数据 :
第一种:
Vue.set(vm._data.student,sex,‘男’);
第二种:
vm.$set(vm.student,sex,‘男’);
局限性:
只能给vm的data里的某个响应式对象添加属性,不能给vm或者data直接添加根对象
editHoppy(){
// this.student.hoppys.splice(0,1,"学习");
this.$set(this.student.hoppys,0,"开车");
}
总结:
Vue监视数据的原理:
vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter实现监视,且 要在new Vuel时 就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下FAPI:
Vue . set(target, propertyName/ index, value) 或
Vm. $set (target, propertyName/ index, value )
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
1.使用这些API:push()、pop()、 shift()、 unshift()、splice()、 sort()、reverse()
2.Vue.set()或vm.$set()
特别注意: Vue.set() 和vm.$set() 不能给vm或vm的根数据对象添加属性! !
收集表单数据:
若: <input type="text" />,则v-mode1收集 的是value值,用户输入的就是value值。
若: <input type="radio"/>, 则v-mode1收集 的是value值,且要给标签配置value值。
若: <input type=" checkbox"/>
没有配置input的value属性,那么收集的就是checked (勾选or未勾选,是布尔值)
配置input的value属性:
(1)v - mode1的初始值是非数组,那么收集的就是checked (勾选or未勾选,是布尔值
(2)v - mode1的初始值是数组,那么收集的的就是va1ue组成的数组
备注: v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输首尾空格过滤
导入依赖
<script src="../js/dayjs.min.js" type="text/javascript">script>
<h1>计算属性实现:{{fmtTime}}h1>
<h1>methods实现:{{mDate()}}h1>
<hr>
<h3>过滤器实现{{date | dataFormat}}h3>
格式化日期格式:
//计算属性
computed: {
fmtTime() {
return dayjs(this.date).format('YYYY年MM月DD日 HH:mm:ss')
}
},
//方法
methods: {
mDate() {
return dayjs(this.date).format('YYYY年MM月DD日 HH:mm:ss')
}
}
//过滤器实现
filters: {
dataFormat(value) {
return dayjs(this.value).format('YYYY年MM月DD日 HH:mm:ss')
}
}
v-test
<div v-test="name">div>
//name属性会替换掉标签中的内容,直接显示在页面上,不会解析str的值里的标签结构
v-html(有安全性问题)
<div v-test="str">div>
str:'<h1>你好h1>' //会解析str的值里的标签结构
v-cloak
<div v-cloak>{{name}}div> //Vue示例一旦接管,就会删除该属性
v-once
<div v-once>{{age}}div> //初次动态渲染后,就变成静态内容了
v-pre
//跳过其节点的编译过程,可使用它跳过没有插值语法,指令语法的节点上,会加快编译
<h1 v-pre>Vue太难了h1>
<div v-once>{{age}}div>
函数式
<h1>10倍是<span v-big="n">span>h1>
//Vue实例中。。。。
directives: {
//自定义指令的两个参数的(当前标签的真实DOM,binding元素和指令的绑定)
//绑定成功时调用,所在的模板被重新解析时调用
big(element, binding) {
element.innerText = binding.value * 10
}
}
对象式
<input type="text" v-fbind:value="n">
directives: {
//自定义指令的两个参数的(当前标签的真实DOM,binding元素和指令的绑定)
//绑定成功时调用,所在的模板被重新解析时调用
big(element, binding,this) { //此处的this是指window
element.innerText = binding.value * 10
},
fbind: {
//指令与元素成功绑定时
bind(element, binding) {
element.innerText = binding.value
},
//指令所在元素被插入页面时
inserted(element, binding) {
element.focus()
},
//指令被重新解析时
update(element, binding) {
element.innerText = binding.value
element.focus()
}
}
}
全局指令:(其他容器也可调用)
<script type="text/javascript">
Vue.directive('fbind', {
//指令与元素成功绑定时
bind(element, binding) {
element.innerText = binding.value
},
//指令所在元素被插入页面时
inserted(element, binding) {
element.focus()
},
//指令被重新解析时
update(element, binding) {
element.innerText = binding.value
element.focus()
}
})
<script/>
注意:
自定义指令如果有多个单词组成用 “ - ” 分割 ,例:big-number
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm或组件实例对象)
流程图:
beforeCreate (初始化数据监视、数据代理之前)
无法通过vm获取data中的数据,和methods的方法,数据代理还未开始。
created(初始化数据监视、数据代理之后)
可以通过vm获取data中的数据,和methods的方法,数据代理、数据监视创建成功。
beforeMounted(挂载之前)
Vue完成模板的解析并把初始的真实DOM元素放入页面之前 (挂载之前)调用beforeMounted
mounted(挂载完毕)
Vue完成模板的解析并把初始的真实DOM元素放入页面后 (挂载完毕)调用mounted,一般发送Ajax请求,开启定时器,自定义事件等等工作。
beforeUpdate(更新之前)
执行eforeUpdate时数据是新的,页面是旧的。页面和数据尚未同步。
updated(更新完毕)
执行eforeUpdate时数据是新的,页面也是新的
beforeDestroy(销毁之前)
此时vm中的data,methods,指令等还可使用,即将被销毁,一般在这阶段关闭定时器,解绑自定义事件等。。。
destroyed(销毁之后)
彻底销毁vm,vm中的数据不再是响应式。原生的DOM依然有效。
template:
//创建模板
template: `
`
,
1.1 组件的定义:
实现应用中的局部功能代码和资源的集合。
1.2 非单文件组件
一个文件中有多个组件
创建非单文件组件:
Vue中使用组件的三大步骤:
一、如何定义一个组件?
使用Vue . extend(options )创建,其中options和new Vue ( options )时传入的那个options几乎-样,但
二、区别如下:
el不要写,为什么?
最终所有的组件都要经过一一个vm的管理,由vm中的el决定服务哪个容器
data必须写成函数,为什么?避免组件被复用时, 数据存在引用关系。
备注:使用template 可以配置组件结构。
如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue . component(‘组件名’,组件)
三、编写组件标签:
<schoo1>school>
示例:
<div id="root">
==》 第三步
<school>school>
<hr>
==》 第三步
<student>student>
div>
<script type="text/javascript">
Vue.config.productionTip = false; //关闭浏览器的生产提示
//1.创建school组件 ==》 第一步
const school = Vue.extend({
// el: "#root" 组件定义时, 一定不要写el 配置项,因为最后所有的组件都由vm决定服务哪个容器
template: `
<div>
<h1>学校名称:{{ schoolName }}h1>
<h1>学校地址:{{ address }}h1>
div>
`,
data() {
return {
schoolName: '北大青鸟',
address: '河南洛阳'
}
}
})
//1.创建student组件 Vue.extend()可省略,直接写里面内容 ==》第一步
const student = Vue.extend({
template: `
<div>
<h1>学生名字:{{ studentName }}h1>
<h1>年龄 {{ age }}h1>
div>
`,
data() {
return {
studentName: '张三',
age: 21
}
}
})
//注册全局组件
// Vue.component('student', student); ==》第二步
//创建vm
const vm = new Vue({
el: "#root",
//2.注册组件(局部注册) ==》第二步
components: {
school: school //简写school
}
})
注意:
关于组件名推荐写法:
一个单词: 纯小写school
多个单词:使用“ - ” 链接并用 ‘’ 包括 : ‘my-school’
MySchool (驼峰命名) 注: 在使用脚手架时才可使用,不然html 页面无法解析;
创建组件简写:
const student = {
template: `
学生名字:{{ studentName }}
年龄 {{ age }}
`,
data() {
return {
studentName: '张三',
age: 21
}
}
}
组件的嵌套
//1.创建student组件
const student = Vue.extend({...})
//2.创建school组件 内嵌套student组件
const school = Vue.extend({
template: `
...//school组件代码
`,
data() {
return {
//...school数据
}
},
components: {
student //注册student组件 (内嵌)
}
})
//创建hello组件
const hello = {
template: `{{msg}}
h1>`,
date() {
return {
msg: '你好李靓仔'
}
}
}
//创建app组件 (一般用于管理所有组件)
const app = Vue.extend({
template: `
//使用组件
`,
components: {
school, //注册组件(嵌套组件)
hello
}
})
//创建vm
const vm = new Vue({
template: ' ',
el: "#root",
components: {
app: app // 2.注册组件(只需注册app组件,它里面嵌套着其他组件)
}
})
VueComponent构造函数
关于VueComponent:
schoo1组件本质是一 个名为YueComponent的构造函数,且不是程序员定义的,是Vue . extend生成的。
我们只需要写
即Vue帮我们执行的: new VueComponent (options)。
特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
关于this指向:
(1).组件配置中:
data函数、methods 中的函数、watch中的函数、computed中的函数它们的this均是[VueComponent实
对象]。
(2).new Vue()配置中:
data函数、methods 中的函数、watch中的函数、computed中的函数它们的this均是[Vue实例对象]。
VueComponent的实例对象,以后简称vc (也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
内置关系
VueComponent.prototype.——proto—— === Vue.prototype
为了让组件实例对象(vc) 也可以访问Vue原型的属性和方法。
单文件组件
一个文件中只有一个组件
创建.vue文件的格式:
//固定格式 创建school组件
//创建App组件
//创建main.js 指定组件要服务的容器
import App from "../vue/App";
new Vue({
el:'#root',
components:{App}
})
//创建容器页面
Vue脚手架是Vue官方提供的标准化开发工具。
**2.1 下载安装Node.js,配置环境 **
(40条消息) NodeJS 安装及环境配置_DistanceZK的博客-CSDN博客_nodejs安装及环境配置
解决报错:
error Component name “School” should always be multi-word vue/multi-word-component-names
在项目根目录下vue.config.js 文件中添加如下代码:
const {
defineConfig
} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false //关闭语法检查
})
关于不同版本的Vue:
vue.js与vue。runtime. xxx. js的区别:
(1) .vue . js是完整版的ue,
包含:核心功能+模板解析器。
(2). vue. runtime . xx. js是运行版的Vue,只包含:核心功能;没有模板解析器。
因为vue.runtime .xx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。
//main.js
import Vue from 'vue' //此 'vue' 是一个残缺版的Vue,它不会解析 模板
import Vue from 'vue/dist/vue.js' //完整版(不推荐)
//使用残缺版 'vue' 则需要在创建实例时配置 (推荐 节省压缩后的体积)
new Vue({
el:'app',
render: h=> h(App)
})
在项目根目录创建 vue.config.js 文件:
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/index/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <%= htmlWebpackPlugin.options.title %>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板会被推导为 `public/subpage.html`
// 并且如果找不到的话,就回退到 `public/index.html`。
// 输出文件名会被推导为 `subpage.html`。
subpage: 'src/subpage/main.js'
}
}
被用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(ve)
使用方式:
打标识:
<h1 ref="xx">.....</h1>或<School ref= "xx"></School>
获取:
this.$refs.xxx
示例:
//App组件
:age = v-bind:age="表达式"
//Student组件
{{msg}}
学生姓名:{{name}}
性别:{{sex}}
年龄:{{age}}
功能:可以把多个组件共用的配置提取成个 混入对象
使用方式:
第一 步定义混合,例如:
//创建mixin.js文件 并将其暴露
export const show = {
methods: {
showName() {
alert(this.name)
}
}
}
第二步使用混入,例如:
(1).全局混入: Vue.mixin(xx)
//在App组件中 引入全局mixin 其子组件也会调用
import {show} from './mixin.js'
Vue.mixin(show)
(2).局部混入: mixins:[ ‘x’]
//组件中
//引入一个mixin
import {show} from '../mixin.js'
export default {
mixins: [show]
}
功能:用于增强Vue
本质:包含instal1方法的一个对象, install的第一个参数是Vue, 第二个以后的参数是插件使用者传递据。
定义插件:
对象.install = function (Vue, options) {
//1.添加全局过滤器
Vue . filter(....)
// 2.添加全局指令
Vue.directive(....)
// 3.配置全局混入(合)
Vue.mixin(....)
// 4.添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xXxx
}
使用插件:
main.js中
//引入插件
import plugins from './plugins.js'
//Vue创建之前,使用插件
Vue.use(plugins)
作用:让样式在局部生效,防止冲突
写法:
//School 组件
<style scoped="scoped">
.demo{
background-color:aqua;
}
style>
//Student 组件
<style scoped="scoped">
.demo{
background-color:aqua;
}
style>
组件化的编码流程
实现静态组件: 抽取组件,使用组件实现静态页面效果.
展示动态数据
2.1 数据的类型? 名称?
2.2 数据的保存在哪个组件
交互------从事件绑定监听开始
props适用于:
(1).父组件==>子组件通信
(2).子组件==>父组件通信(要求父先给子-个函数)
使用v-modeI时要切记: v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推
荐这样做。
tip(小技巧):
<input type="checkbox" :checked="true" />
生成全球唯一标识:
在node终端输入指令: npm i nanoid
//在组件中引入nanoid
import {nanoid} from 'nanoid'
console.log(id: nanoid())
子组件给父组件传递
methods: {
addTodo(todosObj) {
this.todos.unshift(todosObj) //在该VC的todos数组上添加一个由子组件传递过来的对象
}
}
<MyHeader :addTodo="addTodo">MyHeader>
props:['addTodo'],
addTitle(e) {
const todosObj = {
id: nanoid(),
title: e.target.value,
done: false
}
this.addTodo(todosObj)
}
**在浏览器保存数据 **
**第一种 **: (localStorage.setItem)
<button onclick="saveInfo()">点我保存数据button>
<script>
function saveInfo() {
let person = {
name: '张三',
age: 21
}
window.localStorage.setItem('msg', 'hello'); //保存字符串类型
window.localStorage.setItem('number', 20); //保存数值类型,会转为字符串类型
window.localStorage.setItem('person', JSON.stringify(person)); //保存对象类型
}
script>
获取存储的数据:
function getInfo() {
const per = localStorage.getItem('person');
console.log(localStorage.getItem('msg'));
console.log(localStorage.getItem('number'));
console.log(JSON.parse(per));
}
删除存储的数据:
function removeAll() {
localStorage.removeItem('msg');
// localStorage.clear(); //清除全部存储
}
todoList案例本地存储:
data() {
return {
//如果localStorage存储的有值,就取出显示,如果没有就用空数组[] 避免报错
todos: JSON.parse(localStorage.getItem('todos')) || []
}
},
//通过watch 监视属性todos,如果 发生改变 将它存到localStorage中
watch: {
todos: {
deep: true, //深度监视`
handler(value) {
localStorage.setItem('todos', JSON.stringify(value));
}
}
}
**第二种: ** sessionStorage 使用方式和localStorage 一样
区别:
sessionStorage中存储的数据,浏览器关闭就会清除,而localStorage只有 使用removeItem(‘key’) 或者clear(),或者用户清理浏览器缓存才会清除.
11.1 自定义事件绑定:
//Student组件
methods:{
subName(){
//通过$emit('父组件v-on:的xxxx')函数 给父组件传递数据
this.$emit('submitStudentName',this.name)
}
}
//App组件接收
methods:{
getStudentName(name){
alert("学生姓名:"+name)
}
}
第二种:使用ref
//ref方式:
mounted() {
//通过this.$refs.'上方绑定的ref' 获取到student组件的vc实例,
//并给它绑定一个submitStudentName事件,this.getStudentName()是回调函数
this.$refs.student.$on('submitStudentName',this.getStudentName())
}
11.2 自定义事件解绑:
解绑自定义事件: $off()
<button type="button" @click="udbind">解绑传递事件</button>
udbind(){
this.$off('submitStudentName'); //解绑一个自定义事件
this.$off(['submitStudentName','xxxx']) //解绑多个自定义事件
this.$off() //解绑所有自定义事件
}
11.3 总结自定义事件
组件上也可以绑定原生DOM事件,需要使用native修饰符。
注意:通过this. $refs. xx. $on( ’ atguigu’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头
数,否则this指向 会出问题!
第一步:
//在mian.js中,给Vue.prototype添加一个x属性,
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线 this就是当前vm的实例对象
}
})
第二步:
//在要接收的组件中,通过当前VC的$bus傀儡,给传数据的组件绑定'自定义事件'
mounted() {
this.$bus.$on('hello', (data) => {
console.log("我是SChool组件我接受到了", data)
})
}
第三步:
//在要传递数据的组件中,通过当前VC的$bus傀儡.$emit('接收者给它绑定的自定义事件名',要传的数据)
methods: {
subStudent() {
this.$bus.$emit('hello', 666)
}
}
npm i pubsub-js
import pubsub from 'pubsub-js'
//school 组件订阅消息
mounted() {
//subscribe('消息的名字', function(消息的名字,传过来的数据)
this.pubId = pubsub.subscribe('hello', function(msgName, data) {
console.log('@@@@@@@@@@', msgName, data)
})
},
//Stduent组件发布消息
methods: {
subStudent() {
pubsub.publish('hello',555);
}
}
//School 组件 销毁之前 解除消息
beforeDestroy() {
this.unsubscribe(this.pubId);
}
注:
//如过回调函数有两个参数,可以用 _ 来占位
subStudent(_,data) {
pubsub.publish('hello',555);
}
语法: this. $nextTick(回调函数)
作用:在下一次DOM更新结束后执行其指定的回调。
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
<transition name="hello" appear>
<h1 v-show="isShow">你好h1>
transition>
style标签中:
/* 定义动画 */
/* 动画名 */
@keyframes bules{
进入时
from{
transform: translateX(-100%);
}
离开时
to{transform: translateX(0px);}
}
/* 使用.动画标签的name-enter(进入时)-actice{
animation: 动画名 时长 ...
}
*/
.hello-enter-active{
animation: bules 1s;
}
/* 使用.动画标签的name-leave(离开时)-actice{
animation: 动画名 时长 reverse(反转动画) ...
}
*/
.hello-leave-active{
animation: bules 1s reverse;
}
15.1.2 集成第三方动画
1.下载包
npm install animate.css --save
2.在组件中引入
import 'animate.css'
3.指定类名||使用
<transition-group
appear
enter-active-class="animate__wobble"
leave-active-class="animate__backOutDown"
>
<h1 v-show="isShow" key="1">你好h1>
<h1 v-show="isShow" key="2">你好,李靓仔h1>
transition-group>
h1 {
width: 500px;
background-color: darkorange;
transition: 1s linear;
}
/*开始的起点,离开的终点 */
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
/*开始的终点,离开的起点*/
.hello.enter-to,.hello-leave{
transform: translateX(0);
}
15.2.1 多个元素过渡
<transition-group name="hello" appear>
<h1 v-show="isShow" key="1">你好h1>
<h1 v-show="isShow" key="2">你好,李靓仔h1>
transition-group>
1.作用
在插入、更新或移除DOM元素时|在合适的时候给元素添加样式类名。
2.过程 图示:
方法一:
1.在终端下载 axios
npm i axios
2.在组件引入
//引入axios
import axios from 'axios'
3.在项目的vue.config.js中开启代理服务器
//开启代理服务器
devServer: {
proxy: 'http://localhost:5000' //只能配置一个代理
}
4.通过代理获取数据
showInfo() {
//get('此路径是代理服务器的路径,端口和本地一样,执行流程是通过get()请求代理服务器,如果本地项目的public目录下有该要请求的文件,就会停止寻找,如果没有,代理服务去配置的端口的服务器上找,如果请求成功,会通过response函数返回数据,如果失败会通过error函数返回错误信息')
axios.get('http://localhost:8080/students').then(
respone => {
console.log('请求成功',respone.data);
},
error => {
console.log('请求失败',error.message);
})
}
说明:
1.优点:配置简单,请求资源时直接发给前端(8080) 即可。
2.缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
3.工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)
方法二:
在vue.config.js中:
//开启代理服务器(方式二)
devServer: {
proxy: {
'/api': { // 匹配'/api'请求前缀
target: 'http://localhost:5000', //目标路径
//重写路径 :{'^/XXX':''} 代表将所有以XXX开头的路径重写为''空,然后再转发给要请求的服务器
pathRewrite:{'^/api':''},
ws: true, //用于支持websocket
changeOrigin: true //用于检索请求头中的 host 值
},
}
}
// ... 可配置多个
devServer: {
proxy: {
// ...
}
}
通过代理服务器获取数据:
showInfo() {
//请求时要在端口号后带上指定前缀
axios.get('http://localhost:8080/api/students').then(
respone => {
console.log('请求成功',respone.data);
},
error => {
console.log('请求失败',error.message);
})
}
说明:
1.优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
2.缺点:配置略微繁琐,请求资源时必须加前缀。
1.下载 | 引入 | 使用
npm i vue-resource //下载vue-resource
//引入vue-resource
import vueResource from "vue-resource";
//使用插件
Vue.use(vueResource)
2.发送ajax请求
//this.$http.get('url').when(response=>{},error=>{})
searchUsers() {
this.$bus.$emit('ObjInfo', {isFirst: false, isLoding: true, errorMessage: '', users: []})
this.$http.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
// console.log('@@@',response.data.items)
this.$bus.$emit('ObjInfo', {isLoding: false, errorMessage: '', users: response.data.items})
}, error => {
// console.log("请求github失败", error.message
this.$bus.$emit('ObjInfo', {isLoding: false, errorMessage: error.message, users: []})
})
}
18.1 默认插槽
1.作用:让父组件可以向子组件指定位置插入hgm结构,也是一种组件间通信的方式,适用于父组件===>子组件。
2.分类:默认插槽、具名插槽、作用域插槽
<slot>slot>
//父组件
<Category title="美食" :listData="foods">
<img src="https://gimg2.baidu.com/image_search"/>
Category>
18.2 具名插槽
1.给插槽设置name值
<slot name="center">slot>
<slot name="footer">slot>
2.使用
//父组件
<video slot="center" controls width="100%" height="150px" src="https://haokan.baidu.com/v?pd=wisenatural&vid=2285378885613697035">video>
<a slot="footer" href="https://www.baidu.com">动作大片a>
注:
如要包裹多个DOM元素时
//可以使用slot="name" 或v-slot:name
<template v-slot:footer>
<a href="https://www.baidu.com">网络a>
<a href="https://www.baidu.com">单机a>
template>
18.3 作用域插槽
1.理解:数据在组件的自身但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中, 但
用数据所遍历出来的结构由App组件决定)
2.示例
//子组件
//将games数组传递给调用者
<slot :games="games">slot>
//父组件
<Category title="游戏">
<template scope="{games}">
<ul>
<li v-for="(g,index) in games" :key="index">{{ g }}li>
ul>
template>
Category>
vuex三大核心:
Actions Mutations State 它们由Store管理
使用场景:
1.多个组件依赖于同一状态
2.来自不同组件的行为需要变更同一状态
3.执行过程 : 图示
1.下载vuex 3版本(vue2 只能使用 vuex 3版本)
npm i vuex@3
2.搭建vuex环境
示例:
//index.js页面
//引入vuex
import Vuex from "vuex";
import Vue from "vue";
//使用Vuex
Vue.use(Vuex)
//创建actions 用于响应组件的动作
const actions = {
//(上下文,值)
jia(context, value) {
context.commit('JIA', value)
},
}
//准备 mutations 用于存操作数据
const mutations = {
JIA(state, value) {
state.sum += value;
},
}
//准备state 用于存储数据
const state = {
sum: 0 //当前总和
}
//创建Store 管理actions, mutations,state
export default new Vuex.Store({
actions,
mutations,
state
});
//main.js页面
//引入store
import store from "./store/index";
//创建vm
new Vue({
el: '#app',
store, //使用stroe
render: h => h(App),
})
//组件页面
methods: {
addNum() {
this.$store.dispatch('jia', this.num)
},
}
1.概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
2.在store. js中追加getters配置
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex. Store( {
getters
})
3.组件中读取数据:
$store.getters.bigSum
mapState: (与state中的数据生成计算属性)
//引入{mapState}
import {mapState} from 'vuex'
computed: {
//借助mapState生成计算属性 第一种:(对象写法)
// ...代表将一个数组的属性拆分并放入指定位置
...mapState({sum: 'sum', xueke: 'subjectName', school: 'school'})
//借助mapState生成计算属性 第一种:(数组写法)
//生成的计算属性名要和真正读取出来的名要一致
...mapState(['sum', 'subjectName', 'school']),
}
mapGetters (与getters中的数据生成计算属性):
//引入mapGetters和mapActions时 需要传参,参数是要操作的值
<button @click="JIA(num)">+</button>
import {mapState, mapGetters} from 'vuex'
computed{
//对象写法
...mapGetters({bigSum: 'bigSum'})
//数组写法
...mapGetters(['bigSum'])
}
mapActions (生成与actions的对话,即:$store.dispatch(XXX)的函数):
import {mapActions} from 'vuex'
methods: {
//对象写法
...mapActions({JIA: 'JIA'})
//数组写法
...mapActions(['JIA'])
}
mapMutations (生成与mutations的对话,即:$store.commit(XXX)的函数):
//使用mapMutations和
<button @click="JIA(num)">+</button>
import {mapState, mapGetters, mapMutations} from 'vuex'
methods: {
//对象写法
...mapMutations({JIA: 'JIA'})
//数组写法
...mapMutations(['JIA'])
}
模块化+命名空间
目的: 让代码更好维护,分类更明确
修改store.js文件
//人员列表相关的配置
const persion0ption = {
namespaced: true, //开启命名空间
actions: {},
mutations: {...},
state: {...},
getters: {}
}
const countOption = {
namespaced: true ,//开启命名空间
actions: {...},
mutations: {...},
state: {school: '北大青鸟'...},
getters:{...}
}
export default new Vuex.Store({
modules: {
counto: countOption
,
person: persionOption
}
});
//手动写
this.$store.state.counto.sum
//简写借助mapState获取 mapState('store文件中modules的命名',[XXX])
...mapState('counto', ['sum','subjectName','school']),
//手动写 命名规则 getters["store文件中modules的命名/访问目标"]
return this.$store.getters["counto/bigSum"];
//简写借助mapGetters获取 mapGetters('store文件中modules的命名',[XXX])
...mapGetters('counto', {bigSum:'bigSum'}),
//手动写
this.$store.dispatch('counto/jian', this.num)
//简写调用mapActions mapActions('store文件中modules的命名',[XXX])
...mapActions('counto', {jian: 'jian'}),
//手写
this.$store.commit('person/ADD_PERSION',objPer)
//简写调用mapMutations mapMutations('store文件中modules的命名',[XXX])
...mapMutations('counto', {JIA: 'JIA'})
理解:
1.路由就是组key-value的对应关系 。key为路径.value有可能是function或者component
2.多个路由,需要经过路由器的管理。
**1.后端路由: **
2)工作过程: 服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。
**2.前端路由: **
2)工作过程: 当浏览器的路径改变时,对应的组件就会显示。
npm i vue-router@3 //下载router3版本插件,Vue2使用的是router3,Vue3使用的是router4
//引入VueRouter
import VueRouter from "vue-router"
//应用插件
Vue.use(VueRouter);
//该文件用于创建整个应用的路由
//引入VueRouter
import VueRouter from "vue-router"
import About from "@/components/About";
import Home from "@/components/Home";
//创建并暴露一个路由器
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})
//active
<router-link class="list-group-item" active-class="active" to="/about">Aboutrouter-link>
<router-link class="list-group-item" active-class="active" to="/home">Homerouter-link>
<router-view>router-view>
路由组件通常存放在pages文件夹,-般组件通常存放在components文件夹。
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
每个组件都有自己的$route属性,里面存储着自己的路由信息。
整个应用只有一个router, 可以通过组件的$router属性获取到。
1.嵌套路由配置项:
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children:[ //通过children配置子路由
{
path:'news', //注:子路由的path 一定不要写 '/'
component:News,
},
{
path:'message', //注:子路由的path 一定不要写 '/'
component:Message,
}
]
}
]
2.使用嵌套路由 (跳转) :
//to:"要写完整路径"
<router-link class="list-group-item" active-class="active" to="/home/news">Newsrouter-link>
20.4.1第一种 query 传参
query传参的两种方式:
<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}router-link>
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
router-link>
接收参数:
{{$route.query.title}}
20.4.2 路由命名
作用: 简化路由跳转
使用
给路由命名
routes: [
{
name:'guanyu', //给路由命名
path: '/about',
component: About
},
}
简化跳转
to="/****/****/about"
:to="{name:'guanyu'}
配合传参:
<router-link :to="{
name:'xiangqing', //router.js里配置的name
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
router-link>
20.4.3 params 传参:
在router.js文件中
{
name:'xiangqing',
path: 'detail/:id/:title', //使用占位符声明接收params参数
component: Detail,
}
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}router-link>
<router-link :to="{
name:'xiangqing', //router.js里配置的name
params:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
router-link>
{{$route.params.title}}
注:
路由携带params参数时,若使用to的对象写法,则不能使用path 配置,只能使用name配置
**作用:**让组件更方便地接收参数
//props 的第一种写法,值为对象该对象的所有key value 都会以prop形式传递给当前组件
props:{a:'AAA',b:'BBB'}
//props 的第二写法 值为布尔值,若为真将把接收到的所有param参数,以prop形式传递给当前组件
props:true,
//props 的第三写法 值为函数
props($route) {
return {
id: $route.query.id, title: $route.query.title
}
}
组件接收参数:
<li>消息编号:{{id}}li>
<li>消息内容:{{title}} li>
props:['id','title']
1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push 是追加历史记录,replace 是替换当前记录
由跳转时候默认为push
3.如何开启replace模式:
1.作用: 不借助router-link的跳转, 让跳转更加灵活,
2.编码:
//$router.push 堆加路径
pushShow(m) {
this.$router.push({
name: 'xiangqing', //router.js里配置的name
query: {
id: m.id,
title: m.title
}
})
},
//$router.push 替换路径
replaceShow(m) {
this.$router.replace({
name: 'xiangqing', //router.js里配置的name
query: {
id: m.id,
title: m.title
}
})
}
3.可以借助push(),和replace() 像浏览器一样前进和回退
methods:{
back(){
this.$router.back()
},
forward(){
this.$router.forward()
}
go(){
this.$router.go(-2) //根据传递的参数决定进退的次数,正数是进,负数是退
}
}
1.作用: 让不展示的路由组件保持挂载,不被销毁
2.具体编码:
//缓存多个 ['News','about']
<keep-alive :include="News">
<router-view>router-view>
keep-alive>
1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2.具体名字:
1.作用:对路由进行权限控制
2.分类:全局守卫、独享守卫、组件内守卫
3.全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to, from, next)=>{
console.log('beforeEach' ,to, from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage . getItem('school') === 'atguigu'){ //权限控制的具体规则
next() //放行:
}else{
alert( '暂无权限查看')
// next({name: ' guanyu'})
}
}else{
next() //放行
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to, from)=>{
console.log(' afterEach',to, from)
if(to.meta.title){
document.title = to.meta.title 11修改网页的title
}else{
document. title = 'vue_ test'
}})
4.独享路由守卫
//写在单独的router 之中 它没有独显的后置守卫,可配合全局后置守卫使用
beforeEnter: (to, from, next) => {
console.log('前置路由守卫' ,to, from)
if(to .meta. isAuth){ //判断是否需要鉴权
if(localStorage . getItem('school ' )=== ' atguigu'){
next()
}else{
alert('学校名不对,无权限查看! ')
}else{
next( )
}
}
5.组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
对于个url来说,什么是hash值?一#极 其后面的内容就是hash值。
hash值不会包含在HTTP请求中,即: hash值不会带给服务器。
hash模式:
1.地址中永远带着#号,不美观。
2.若以后将地址通过第3 E方手机app分享,若app校验严格,则地址会被标记为不合法。
3.兼容性较好。
1.地址干净,美观。
2.兼容性和hash模式相比略差。
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
Vant https://youzan.github.io/vante
2.
Cube UI https://didi.github.io/cube-ui
3.
Mint UI http://mint-ui.github.io
Element UI https://element.eleme.cn
2.
IView UI https://www.iviewui.com
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
…
使用Proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-Shaking
…
Composition API(组合API)
新的内置组件
其他改变
官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
const xxx = ref(initValue)
xxx.value
{{xxx}}
Object.defineProperty()
的get
与set
完成的。reactive
函数。ref
函数)const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)实现原理:
对象类型:通过Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
存在问题:
实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
MDN文档中描述的Proxy与Reflect:
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'
reactive
转为代理对象。Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。.value
,读取数据时模板中直接读取不需要.value
。.value
。setup执行的时机
setup的参数
this.$attrs
。this.$slots
。this.$emit
。与Vue2.x中computed配置功能一致
写法
import {computed} from 'vue'
setup(){
...
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
与Vue2.x中watch配置功能一致
两个小“坑”:
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
*/
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
watch的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
1
beforeDestroy
改名为 beforeUnmount
destroyed
改名为 unmounted
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
类似于vue2.x中的mixin。
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
语法:const name = toRef(person,'name')
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
扩展:toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
什么时候使用?
reactive
生成的响应式对象转为普通对象。作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
实现防抖效果:
{{keyword}}
作用:实现祖与后代组件间通信
套路:父组件有一个 provide
选项来提供数据,后代组件有一个 inject
选项来开始使用这些数据
具体写法:
祖组件中:
setup(){
......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
......
}
后代组件中:
setup(props,context){
......
const car = inject('car')
return {car}
......
}
reactive
创建的响应式代理readonly
创建的只读代理reactive
或者 readonly
方法创建的代理使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
什么是Teleport?—— Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。
我是一个弹窗
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤:
异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用Suspense
包裹组件,并配置好default
与 fallback
我是App组件
加载中.....
Vue 2.x 有许多全局 API 和配置。
例如:注册全局组件、注册全局指令等。
//注册全局组件
Vue.component('MyButton', {
data: () => ({
count: 0
}),
template: ''
})
//注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
}
Vue3.0中对这些API做出了调整:
将全局的API,即:Vue.xxx
调整到应用实例(app
)上
2.x 全局 API(Vue ) |
3.x 实例 API (app ) |
---|---|
Vue.config.xxxx | app.config.xxxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
data选项应始终被声明为一个函数。
过度类名的更改:
Vue2.x写法
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
Vue3.x写法
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
移除v-on.native
修饰符
父组件中绑定事件
子组件中声明自定义事件
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
…