Vue脚手架
初始化脚手架
说明
-
Vue脚手架是Vue官方提供的标准化开发工具(开发平台)。
-
脚手架最新版本是4.x。(截止到2022年)
-
Vue版本和脚手架版本不用一一对应,一般选择新版本的脚手架。
-
Vue脚手架官方文档:https://cli.vuejs.org/zh/
cli:command line interface
即为脚手架。
具体步骤
-
第一步(仅第一次执行):全局安装
@vue/cli
npm install -g @vue/cli
-
第二步:**切换到你要创建项目的目录**,使用命令创建项目
vue create xxx
-
第三步:进入创建的项目
xxx
内npm run serve
分析脚手架结构
-
上述各个内容
-
public
favicon.ico
页签图标index.html
应用页面(单页面不代表功能单一,以后都是开发的单页面)
-
src
main.js
整个项目的入口文件。npm run serve
就是执行main.jsasstes
存放静态资源components
存放组件。但是不存放App.vue,这个单独存放在外层
-
babel.config.js
-
package-lock.json
包版本控制文件 -
package.json
-
README.md
项目说明
-
render配置项
-
目标:学习
main.js
中的的render
配置项new Vue({ el: '#app', render: h => h(App), })
-
脚手架提供的默认引入方法
import vue from 'vue'
引入的vue是残缺版的vue当在main.js写下面的代码时,使用残缺版的代码会报错,无法渲染。
残缺版的Vue不支持
tempalte
模板功能,无法解析,因为没有模板解析器。new Vue({ el: '#app', template:'<h1>你好</h1>', // components:{ // App // } })
-
无法渲染模板解决方法
-
手动引入完整版的vue
import Vue from 'vue/dist/vue.js'
-
使用render(),推荐
// 普通函数写法 render(createElement){ return createElement('h1','你好啊') } // 箭头函数写法,常用,因为里面不需要用到this render: h => h(App)
注意
- 是不能解析
main.js
里面vm
的template
模板 - 对于
.vue
组件里面的template
标签,有专门的库用于解析
-
为什么推荐第二种方法?
Vue由两部分组成:核心功能+
template
模板解析器 占了$1/3$的体积-
带common的是common语法
-
带esm的是es6语法
-
带有runtime的是运行时版本,不包含模板解析器
-
-
总结
关于不同版本的Vue:
-
vue.js和vue.runtime.xxx.js的区别:
-
vue.js
是完整版的Vue
,包含:核心功能+模板解析器。 -
vue.runtime.xxx.js
是运行版的vue
,只包含:核心功能,没有模板解析器。Vue.runtime.xxx.js
没有模板解析器,所以vm
不能使用template
配置项需要使用
render
函数接收到createElement
函数去指定具体内容。
-
脚手架基础配置
vue inspect > output.js
该命令会输入一个output.js对象,里面有vue隐藏起来的配置。
配置参考网站:https://cli.vuejs.org/zh/config/
脚手架里面的不能改的东西:
public文件夹和它下面的内容,即public的全部
src文件夹 它下面的内容可以改
main.js文件名不能改
约定优于配置,其他东西也不建议改。
需要改:
语法检查要关闭,比如定义了一个变量但是没用也会报错,所以不是运行的最终代码就给关掉。
ref属性
- 作用:用来给节点(DOM节点、组件节点)打标识
- 读取方式:
this.$refs.xxx
,该代码写在Vue
代码中可以获得对应标识的节点
总结
-
被用来给
元素
或子组件
注册引用信息(使用id的获得DOM的替代者) -
获得内容:
- 应用在
html标签上
获取的是真实DOM元素 - 应用在组件标签上获得的是
组件实例对象(vc)
- 应用在
-
使用方式:
打标识:
<h1 ref='xxx'>...</h1>
或<School ref='xxx'></School>
获取:
this.$refs.xxx
控制台有时候不会自动刷新,需要手动刷新。
props属性
-
作用:父组件给子组件传递数据
-
父组件传递数据的方式:
在父组件的
template
标签下,使用子组件-
全写
<son v-bind:xxx=''/ >
-
简写
<son :xxx=''/ >
将等号后面的数据传递给子组件
props
中的xxx
属性. -
-
子组件接受数据的方式:
通过
props
配置属性接受// 1.接受数据不做任何限制 props:['name'] // 2.限制类型 props:{ name:String } // 3.限制类型、必要性、默认值 props:{ name1:{ type:String, // 数据类型 required:true, // 必要性 default:'18', // 默认值 }, }
-
注意点
props接收得到的数据是不能修改的。改了有效果,但是控制台会报错,不建议。
-
场景:如果有业务需求,需要改接受到的数据怎么办?
-
解决方法:借助
data(){}
-
使用
this.xxx
获得传入的props
数据 -
将
this.xxx
赋值给data
中的数据 -
修改
data
对应数据的值
<template> <div> <h2>学生年龄:</h2> <button @click="updateAge">点击修改年龄</button> <hr /> </div> </template> <script> export default { name: "Student", data() { return { myAge: this.age, }; }, props: ["name", "age", "sex"], methods: { updateAge() { // this指向组件对应的实例化对象,用vc来指代。 this.myAge++; }, }, }; </script>
这些数据都会挂载到
vc
实例对象上,所以在Vue实例通过this.xxx
获得。 -
-
-
注意点2
props
属性名避免和data
中属性同名,会报错;优先级:如果同名,
props
>data
,会优先显示props
中的数据。
mixin混合
组件自己已经是复用代码,如果组件里面的代码还想要复用,那么就使用混合技术。
混合就是在src目录下再写一个.js
文件,想需要复用的配置写该在.js
文件中,之后暴露、引用。
总结
-
功能:可以把多个组件共用的配置提取成为一个混合对象
-
使用步骤:
-
定义混合
举例:
// 哪一种暴露方法都可以 // Vue组件的任何配置都可以写成混合 export const hunhe2 = { data() { return { x: 100, y: 200 } } }
-
使用混合
- 全局混入:
import
导入模块后,配置Vue.mixin(xxx)
- 局部混入:
import
导入模块后,在Vue组件中配置mixins:['xxx']
- 全局混入:
-
插件
-
定义:
Vue
插件是一个包含install
方法的对象 -
约定:定义在
src
文件夹下,并命名为plugins.js
。 -
通过
install
方法给Vue
或Vue
实例天极爱方法,定义全局指令等.
总结
-
功能:用于增强Vue
-
本质:
-
包含install方法一个对象
-
install的第一个参数是
Vue
,第二个以后的参数是插件使用者传递的数据。
-
-
定义插件:
-
使用插件:
Vue.use()
样式
scoped
-
问题:多个Vue组件中的样式最终会汇总到一起;如果不同组件中样式名字发生冲突,后面引入的会覆盖前面引入的。
-
解决方法:在组件
.vue
的style
标签中写scoped
属性,就可以解决冲突。注意:
App
组件不适合写scoped
less
lang
可以指定css的编写方式
<style lang='less'>
// 这里面写less的css
</style>
但是Vue
无法解析less
,还需要使用npm
包管理工具安装less-order
总结
-
scoped
作用:让样式在局部生效,防止冲突 -
scoped
写法:<style scoped> // 里面写css代码 </scoped>
Todolist案例
通过这个案例学习组件化编码流程,组件化编码流程:
-
实现静态组件:抽取组件,使用组件实现静态效果。
组件命名的方式:
-
按照功能,如添加
add
、列表list
-
按照在页面中的位置,一般都是首尾按位置命名。
Header、Footer、MyFooter
不能用,因为H5中已经使用过了,Myxxx
不易理解不好,建议将My
改成其他名词。
实际开发中,html和css代码的使用方式:
-
已有的
html
全部复制粘贴到app
组件的template
中,后面再划分。 -
已有的
css
样式全部复制粘贴到app
组件的style
中,后面再换分。
-
如果名字很难起,那么很可能是组件划分的不够好。
-
展示动态数据
- 数据的类型、名称
- 类型:存储数据一般都是对象和数组,比如对象中存储数组对象,很常见。
- 数据保存在哪个组件
- 一个组件在用:放在组件自身即可。
- 一些组件在用:放在他们共同的父组件上(状态提升)。
- 数据的类型、名称
-
交互——从绑定事件监听开始
-
获得用户输入的表单数据的方法
-
方法1:事件对象
fn(e) { // 获得用户表单输入数据的方法 console.log(e.target.value); }
-
方法2:
v-model
双向绑定<template> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add" /> </div> </template> <script> export default { // 部分略 data() { return { title: ""} }, methods: { add() { // 获得用户表单输入数据的方法 console.log(this.title); }, }, }; </script>
-
-
没有服务器时(单机),怎么设定用户id
-
根据时间设置
-
随机数
-
uuid
或nanoid
,使用这两个需要使用npm
安装包// 调包 import { nanoid } from "nanoid"; // 使用 const todoObj = { id: nanoid() };
-
-
组件之间传递数据
-
父传给孩子
父组件做的时间:在父组件
template
标签中,使用v-bind
的方式给子组件实例化对象传递数据。// 父组件 <div class="todo-wrap"> // 在下面的组件标签中写:变量名=数据的方式 <my-header :a='123'/> <List /> <MyFooter /> </div>
子组件做的操作:在子组件中使用
props
属性接受数据// 子组件 props:['a'] // 这个a要和v-bind后的变量对应
-
儿子给父亲传东西:
-
父亲给儿子一个函数,该函数定义在父亲中。
这个函数需要有形参,形参就是父子传递的数据。
-
儿子
props
接受到函数,调用函数传递数据。
https://www.bilibili.com/video/BV1Zy4y1K7SH?p=72&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705
-
-
兄弟之间
消息队列、全局机制
本节使用:
props
的方式,将数据从父亲传递给两个兄弟,兄弟再传递给父亲,通过父亲通信。
-
-
完善,表单有个值又改又设置
计算属性的setter和getter(完善 )
https://www.bilibili.com/video/BV1Zy4y1K7SH?p=76&spm_id_from=pageDriver&vd_source=dde2f4dd432156027fedf9b1734ba705
-
总结
-
组件化编码流程:
-
拆分静态组件:1.按照功能划分 2.命名不要和
html
元素冲突。 -
实现动态组件:考虑好数据存放的位置
1)一个组件在用,放在组件自身
2)一些组件在用,放在他们共同的父组件上
-
-
props
适用于:1)父组件 ==> 子组件 通信
2)子组件 ==> 父组件 通信
要求父先给子一个函数,这些函数之间通过**函数参数**和this通信 -
使用
v-model
时要切记:v-model
绑定的值不能是props
传过来的值;因为props
是不可以修改的! -
props
传过来的值若是对象类型的值,修改对象中的属性时Vue
不会报错,但不推荐这样做。
约定:不修改
props
的值
webStorage
本地存储不是vue
团队打造的,而是在js
中就有.
webStorage
包括localStorage
和sessionStorage
localStorage
-
操作有四个:
添加数据、获得数据、删除一条数据、清空所有数据.
-
特点:
- 闭浏览器,数据依然存在;
- 用户在浏览器清除缓存会消失,
cookie及其他网站数据
选项
// 1.添加数据
localStorage.setItem('key', 'value')
// 2.获得数据
localStorage.getItem('key')
// 3.删除一条数据
localStorage.removeItem('key')
// 4.清空数据
localStorage.clear()
// 注意,针对对象类型的数据,存储和获取时需要借助JSON
// 0.1存储
let p = { name: '张三', age: 22 }
localStorage.setItem('person', JSON.stringify(p)) // 否则会被系统存储为不认识的[object Object]
// 0.2读取
JSON.parse(localStorage.getItem('person'))
sessionStorage
操作:API和上述localStorage
相同,只是需要把local
换成session
特点:只要关闭浏览器,里面存储的数据就会消失.
组件自定义事件
自定义事件和Vue
中的内置事件相对应。
-
给谁用
内置事件给
html
元素用,自定义事件给组件
用. -
谁触发事件
组件的实例化对象
vc
.
绑定自定义事件
-
父组件
-
在父组件中,给子组件的实例化对象绑定自定义事件和回调函数
// 第一种写法 <template> <div class="app"> // 在父组件的student实例对象中绑定事件和回调函数 <Student @atguigu="demo" /> </div> </template>
// 第二种写法 <template> <div class="app"> <Student ref="student" /> </div> </template> <script> import Student from "./components/Student"; export default { // 部分略 // 另外一种给子组件绑定事件的方法 mounted() { this.$refs.student.$on("atguigu", this.demo); }, }; </script>
-
在回调函数的形参中接受从子组件传递的数据
<script> import Student from "./components/Student"; export default { // 部分略 methods: { // 回调函数 demo(name) { console.log("APP收到了学生名:", name); }, }, };
-
有时候子组件可能传递很多数据,可以使用
...other
,将其他的demo(name,...other) // other是一个保存其他数据的数组
-
-
-
在子组件中触发自定义事件
// 子组件的函数中触发自定义事件 this.$emit("自定义事件名") this.$emit("自定义事件名",传递的数据) // 一般将传递的数据包裹成{}对象类型
解绑自定义事件
-
在谁身上解绑?
父组件给哪个子组件绑定的事件,就去哪个子组件中写方法解绑
-
语法
-
解绑一个自定义事件
this.$off('事件名')
-
解绑多个自定义事件
this.$off(['事件1','事件2'])
-
同时解绑所有自定义事件
this.$off()
-
自定义事件总结
``插值语法中可以用的
xxx
的来源
data
最先定义的数据
props
父组件传过来的数据
computed
根据已有的数据计算得到的数据子组件传递过来的数据
在data中定义
xxx
为空,在传给子组件的函数体中使用
this.xxx
=’子组件传递过来的数据’,
this
将xxx
挂在到了data
上(因为事先已经定义了data.xxx
)
注意点
-
传给子组件函数的写法
-
以普通函数的形式写在父组件的
methods
方法中,可以保证这个函数体里面的this
指向该实例化对象.推荐// 第一种方法,即直接在模板中给子组件实例对象绑定事件 <Student @atguigu="demo" />
vue
规定: 写在methods
中的普通函数的this
一定指向methods
所在位置的实例对象 -
以箭头函数的形式写在子组件
$on
方法绑定的回调函数中// 第二种该方法,使用$refs给子组件绑定事件 this.$refs.student.$on('atguigu',(name)=>{ this.xxx='name' })
-
-
是否可以传递内置事件?
-
内置事件在传递时会默认为自定义事件
-
在内置事件后添加
.native
修饰符,就可以变成自定义事件
-
总结
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:
-
A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
-
在父组件中给子组件绑定自定义事件
-
-
绑定自定义事件:
-
第一种方式,在父组件中
<Demo @atguigu="test"/>
<Demo v-on:atguigu="test"/>
-
第二种方式,在父组件中:
<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on('atguigu',this.test) }
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。
-
-
触发自定义事件:
this.$emit('atguigu',数据)
-
解绑自定义事件
this.$off('atguigu')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
全局事件总线
-
作用:可以实现任意组件间通信
-
原理图
-
下图中左侧是有关的组件,
x
是和任何一个组件都没关系的需要满足一定条件的东西. -
X
需要满足的条件- 所有组件可见
X
x
可以调用$on
、$off
、$emit
实现上面两点就实现了事件总线
- 所有组件可见
- 当D想给A传数据时,操作流程如下:
-
在组件
A
中给X
注册自定义事件、回调函数为demo
; -
当D想给A传数据,D触发X的自定义事件
demo
,传递数据6
过去.
-
-
事件总线应该放在哪里?
-
window
上不太好,很少向window上放东西
-
VueComponent
构造函数的原型对象上?-
VueComponent
构造函数是Vue.extend
自动生成的,程序员没办法手动调用VueComponent.xxx
, -
并且每次
Vue.extend
都是生成的全新的VueComponent
所以事件总线不能放在
VueComponent.prototype
上. -
-
Vue
的构造函数,即Vue.prototype
上?- 因为
VueComponent.prototype.__proto__=Vue.prototype
,而且程序员可调用Vue
事件总线的代码应该写在
main.js
,因为从哪里引入Vue就在哪里写. - 因为
-
总结笔记未整理
消息订阅与发布
实现任意组件间通信的技术
要分清
需要消息的人(接受数据方)
发送消息的人(发送数据方)
Vue
没有原生方法,使用第三方库pubsun-js
,这个库可以实现在任何框架中
步骤:
订阅消息:需要数据的人去订阅消息,指定消息名
发布消息:发送数据的人,给指定的消息名发送数据
订阅的时候是什么名,发布的人就使用什么名.
B发送消息触发A的回调函数
接收方代码:
// 订阅消息
mounted:{
// publishid
// hello消息名
// 回调函数,通过参数获得发送方发送的数据,msgName消息名,data发送方传递的数据
// pubsub引入的库
this.publishid = pubsub.subscribe('hello',function(msgName,data){
// 操作,略
console.log(this) // 输出undefined,因为实在pubsub库中用的函数,不保证this指向
})
}
// 取消订阅
beforeDestroy() {
// 取消订阅,定时器
pubsub.undescribe(this.pubId);
},
this指向的问题
问题:如果想让 pubsub的this指向Vue的实例对象怎么办?
方法:
- 把pubsub.subscribe的回调函数写成箭头函数的形式
- 在methods定义一个函数,然后在pubsub中调用这个函数,如this.demo
发送方代码:
// 业务:点击按钮发送消息
methods:{
sendMessage(){
// 执行发送消息的函数,发送666给订阅者
pubsub.publish('hello',666)
}
}
总结
全局视总线和消息订阅与发布都可以实现任意组件的通信.
应该选择哪个
建议全局事件总线,因为这是Vue中自带的东西
nextclick
vue操作过程:等到回调函数全部执行完毕,才会重新渲染模板;这样做的好处时:如果改变多次数据,那么就要重新解析模板多次,太花费时间了.所以等到回调执行完统一重新解析模板.
解决方法
1.使用定时器
不给时间的定时器,给时间也可以
2.使用$nextTick
$nextTick
中的回调函数会等待模板解析完后再执行
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调。
- 什么时候用:当改变数据后,要==基于更新后的新DOM进行某些操作==时,要在nextTick所指定的回调函数中执行。
过渡与动画
显示与隐藏
动画效果
vue提供的功能:
在节点在显示和隐藏的时候自动调用 v-enter-active
或v-leave-active
加载动画
而不是自己手写js控制现在应该添加显示样式还是隐藏样式.
在页面加载的初次显示时,就使用显示动画,那么使用appear
属性
在显示和隐藏时自动调用:
不给transion
指定名字,默认去找v-enter-active
和v-leave-active
/* 整个进入的过程 */
.v-enter-active
/* 整个离开的过程 */
.v-leave-active
过渡效果
过渡效果:在一定时间内实现元素状态转变为最终状态
/* 进入 */
.v-enter 进入起点
.v-enter-to 进入终点
/* 离开 */
.v-leave 离开起点
.v-leave-to 离开终点
进入起点的位置和离开终点位置相同
进入重点和位置和离开起点位置相同
因此,样式可以写成这样:
.v-enter , .v-leave-to {
}
.v-enter-to , .v-leave {
}