第三章、使用脚手架
1、初始化脚手架
1、具体步骤
1、第一步
1、只执行一次,就是设置全局安装
@vue/cli
2、第二步
1、切换到需要创建项目的目录,然后使用创建命令
vue create xxxx
3、第三步
1、启动项目
npm run serve
4、注意
1、如果下载慢,就使用淘宝的镜像
npm config set registry https://registry.npm.taobao.org
2、脚手架的结构
1、结构

2、Render函数
1、是用来帮app组件创建容器的一个方法

2、一般只有在这才用
3、不同版本的Vue
1、vue.js和vue.runtime.xxx.js的区别
1、vue.js是完整的vue。包含核心功能和模板解析器
2、vue.runtime.xxx.js是运行版的vue,只包含:核心功能、没有模板解析器
2、注意
1、因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
4、vue.config.js配置文件
- 使用vue inspect > output.js可以查看到Vue脚手架的默认配置。
- 使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh
3、ref属性
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
- 使用方式
打标识:```<h1 ref="xxx">.....</h1>```
```<School ref="xxx"></School>```
获取:```this.$refs.xxx```
4、props配置项
1、功能
- 让组件接受外部传过来的数据
2、使用
1、传递数据
<Demo name="xxx">
2、接受数据
1、方法一(只接收)
props:['name']
2、方法二(限制类型)
props:{
name:String
}
3、方法三(限制类型、限制必要性、指定默认值)
props:{
name:{
type:Stirng,
required:true,
default:'xxx'
}
}
3、注意
1、props只读,vue底层逻辑会监视你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那就赋值props的内容到data中,然后去修改data的数据
2、props中的方法三,required和default尽量不要写在一起
5、mixin(混入)
1、功能
1、可以把多个组件共用的配置提取成一个混入对象
2、可以理解为java中的封装
2、使用格式
1、定义混合
{
data(){...},
methods:{...},
......
}
2、使用混合
全局混合:Vue.mixin(xxx)
局部混合:mixins:['xxx']
6、插件
1、功能
1、用于增强Vue
2、本质
1、包含install方法的一个大对象,install的第一个参数是vue,第二个以后的参数是插件使用者传递的数据
3、定义插件
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
4、使用插件
1、格式
Vue.use()
7、TodoList案例
1、实现数据的添加
1、重点

2、就是Header接收数据,传递给List中的item显示,
①就需要把数据放到App中,然后App再把数据传递给list中的item
②Header无法直接给List(目前所学)
③App给Header中传入了一个函数
④在Header所需要的时候,再传给App组件
8、总结TodoList案例
1、组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
2、props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
-
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
-
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
9、webStorage
1、内容
- 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
- 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
2、相关API
-
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。 -
xxxxxStorage.getItem('person'); 该方法接受一个键名作为参数,返回键名对应的值。
-
xxxxxStorage.removeItem('key'); 该方法接受一个键名作为参数,并把该键名从存储中删除。
-
xxxxxStorage.clear() 该方法会清空存储中的所有数据。
3、备注
- SessionStorage存储的内容会随着浏览器窗口关闭而消失。
- LocalStorage存储的内容,需要手动清除才会消失。
如果xxx对应的value获取不到,那么getItem的返回值是null。xxxxxStorage.getItem(xxx)的结果依然是null。JSON.parse(null)
10、组件自定义事件
1、内容
1、是什么:一种组件间通信的方式,适用于:子组件 ===> 父组件
2、使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
2、绑定自定义事件
1、方法一
1、在父组件中:
<Demo @atguigu="test"/> 或 <Demo v-on:atguigu="test"/>
2、方法二
1、在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
3、注意
1、若想让自定义事件只能触发一次,可以使用方法。once`修饰符,或`$once
3、触发/解绑自定义事件
1、触发自定义事件:
this.$emit('atguigu',数据)
2、解绑自定义事件:
this.$off('atguigu')
3、组件上也可以绑定原生DOM事件,需要使用修饰符native
4、注意
1、通过绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!this.$refs.xxx.$on('atguigu',回调)
11、全局事件总线
1、是什么
1、一种组件间通信的方式,适用于任意组件间通信。
2、怎么做
1、安装全局事件总线
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
2、使用事件总线
1、接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
2、格式
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
3、提供数据:this.$bus.$emit('xxxx',数据)
3、注意
1、最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
4、TodoList例子
1、App.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader @addTodo="addTodo"/>
<MyList :todos="todos" />
<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
</div>
</div>
</div>
</template>
<script >
import MyList from "./components/MyList.vue";
import MyFooter from "./components/MyFooter.vue";
import MyHeader from './components/MyHeader.vue';
export default {
name:'App',
components: {
MyHeader,
MyFooter,
MyList
},
data(){
return{
//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
todos:JSON.parse(localStorage.getItem('todos')) || []
}
},
methods:{
// 这个方法就是添加一个todoObj
addTodo(todoObj){
this.todos.unshift(todoObj)
},
// 勾选or取消勾选一个todo
checkTodo(id){
this.todos.forEach((todo)=>{
if(todo.id===id){
todo.done = !todo.done
}
})
},
//删除一个todo
deleteTodo(id) {
this.todos = this.todos.filter( todo => todo.id !== id )
},
// 全选或全不选
checkAllTodo(done){
this.todos.forEach((todo)=>{
todo.done = done
})
},
// 删除所有的todo
clearAllTodo(){
this.todos =this.todos.filter((todo)=>{
return !todo.done;
})
}
},
watch:{
todos:{
deep:true,
handler(value){
localStorage.setItem('todos',JSON.stringify(value))
}
}
},
mounted(){
this.$bus.$on('checkTodo',this.checkTodo),
this.$bus.$on('deleteTodo',this.deleteTodo)
},
beforeDestory(){
this.$.bus.$off('checkTodo')
this.$.bus.$off('deleteTodo')
}
};
</script>
<style>
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
2、MyItem.vue
<template>
<li>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<!-- <input type="checkbox" v-model="todo.done"/> -->
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" @click="handleDelet(todo.id)">删除</button>
</li>
</template>
<script>
export default {
name:'MyItem',
// 声明接收todo数据
props:['todo'],
methods:{
handleCheck(id){
// // console.log(id)
// 通知App组件将对应的todo对象的done值取反
// this.checkTodo(id);
this.$bus.$emit('checkTodo',id)
},
// 删除item
handleDelet(id){
if(confirm("确定删除吗?")){
// this.deleteTodo(id)
this.$bus.$emit('deleteTodo',id)
}
// console.log(id)
}
}
}
</script>
<style>
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
li:hover{
background-color: #ddd;
}
li:hover button{
display: block;
}
</style>
3、MyList.vue
<template>
<ul class="todo-main">
<MyItem
v-for="todoObj in todos"
:key="todoObj.id"
:todo="todoObj"
/>
</ul>
</template>
<script>
import MyItem from './MyItem.vue'
export default {
name:'MyList',
components:{
MyItem
},
props:['todos']
}
</script>
<style>
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
12、消息订阅与发布(pubsub)
1、是什么
1、一种组件间通信的方式,适用于任意组件间通信。
2、是个插件,相当于jar包
2、怎么用
1、安装
1、步骤
npm i pubsub-js
2、使用
1、引入
import pubsub from 'pubsub-js'
2、接受数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
3、格式
methods(){
demo(data){......}
}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
4、提供数据
pubsub.publish('xxx',数据)
3、注意事项
1、最好在beforeDestroy钩子中,用去取消订阅。PubSub.unsubscribe(pid)
2、一般还是使用vue的全局总线,因为他们两个一样,但是pubsub需要处理this,这个包不是vue中的this,所以可能会出现问题。
13、nextTick
1、是什么
1、是一个vue中的一个api函数
2、作用:在下一次 DOM 更新结束后执行其指定的回调。
2、怎么用
1、什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
14、Vue封装的过度与动画
1、是什么
1、就是过度的动画
2、怎么用
1、作用
1、在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
2、图示(官网)

3、写法
1、准备好样式
1、元素进入的样式
v-enter:进入的起点
v-enter-active:进入的过程中
v-enter-to:进入的终点
2、元素离开的样式
v-leave:离开的起点
v-leave-active:离开的过程
v-leave-to:离开的终点
2、使用<transition>包裹要过度的元素,并配置name属性
<transition>1、
<transition name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
4、备注
1、若有多个元素需要过度,则需要使用:值。<transition-group>`,且每个元素都要指定`key
5、例子
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
</div>
</template>
<script>
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
</script>
<style scoped>
h1{
background-color: orange;
}
/* 进入的起点、离开的终点 */
.hello-enter,.hello-leave-to{
transform: translateX(-100%);
}
.hello-enter-active,.hello-leave-active{
transition: 0.5s linear;
}
/* 进入的终点、离开的起点 */
.hello-enter-to,.hello-leave{
transform: translateX(0);
}
</style>
3、使用第三方的过度与动画
1、官网:Animate.css | A cross-browser library of CSS animations.
1、安装
npm install animate.css --save
2、导入
import 'animate.css'
3、使用
<head>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
</head>
4、例子
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
</div>
</template>
<script>
import 'animate.css'
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
</script>
<style scoped>
h1{
background-color: orange;
}
</style>