1. 初始化脚手架
1.1. 安装
- 初次使用前,全局安装@vue/cli
npm install -g @vue/cli
- 切换到要创建项目的目录,然后创建项目
vue create xxx
- 进入项目目录并启动
cd xxx
⟶ \longrightarrow ⟶npm run serve
备注:
设置淘宝镜像npm config set registry https://registry.npm.taobao.org
一般用两次Ctrl + c
停掉脚手架。
1.2. 项目文件夹
.gitignore
git 的忽略文件,配置不受git管理的文件或文件夹;babel.config.js
babel 的控制文件,配置参考 Vue CLI / Babel;package-lock.json
包版本控制文件;package.json
应用包配置文件;vue.config.js
Vue 的控制文件,配置参考 Vue CLI;
Vue 脚手架隐藏了所有 webpack 配置,若想查看具体配置,可以执行
vue inspect > output.js
。所有在配置参考中出现的属性都可以在vue.config.js
中自己设置,之后会用其中的设置替换掉默认配置。修改完之后要重新启动脚手架。其他的配置不可以改嗷!
- src
main.js 执行完npm run serve
命令之后直接运行;
assets 存放静态资源,图片、视频等;
components 存放除了 App.vue 之外的所有组件;
创建项目时,main.js 中默认引入的是运行版本的 Vue, 因此如果用 template 配置模板,会报错 “ 缺少模板解析器 ”,解决方式有两种:引入完整版的vue
import Vue from 'vue/dist/vue'
;用render
函数进行渲染。bilibili
vue.js 是完整版的 Vue,包含核心功能和模板解析器,vue.runtime.xxx.js 是运行版的 Vue,只包含核心功能,没有模板解析器;
vue.runtime.xxx.js 不能使用 template 配置项,需要用 render 函数接收到的 createElement 函数去指定具体内容。
- public
xxx.icon 网页的页签图标;
index.html 整个应用的界面。
文件目录
learn
|-- README.md
|-- babel.config.js
|-- jsconfig.json
|-- node_modules
|-- package-lock.json
|-- package.json
|-- public
| |-- index.html
| |-- kirlant.ico
|-- src
| |-- App.vue
| |-- assets
| | |-- logo.png
| |-- components
| | |-- School.vue
| | |-- Student.vue
| | |-- Wecome.vue
| |-- main.js
|-- vue.config.js
2. 使用
2.1. ref属性
- 给元素或子组件注册引用信息,是 id 的替代者;
- 应用在 html 标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象;
- 获取:
this.$refs.xxx
<!-- App.vue -->
<template>
<div class="app">
<Welcome/>
<School ref="sch" />
<Student id="stu" />
<div class="show">
<h3 v-text="msg" ref="title"></h3>
<button ref="btn" @click="showDOM">show upper DOM</button>
</div>
</div>
</template>
<script>
// 引入组件
import Welcome from './components/Wecome';
import School from './components/School';
import Student from './components/Student';
export default {
name:'App',
data(){
return{
msg:"Welcome to yyt's test"
};
},
components:{
Welcome,
School,
Student
},
methods:{
showDOM(){
console.log(this.$refs.title); // 真实DOM元素
console.log(this.$refs); // 真实DOM元素
console.log(this.$refs.sch); // school组件的实例对象
}
}
}
</script>
<style>
.app{
width: 400px;
border: 2px solid #aaa;
padding: 10px 0;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-content: center;
}
.show{
border-top: solid 1px #aaa;
text-align: center;
}
</style>
2.2. prop
让组件接收外部传过来的数据。
- 传递数据
<Demo name="xxx"/>
- 接收数据
// 简单声明接收
props:['name']
// 接收的同时对数据进行类型限制
props:{
name:String
}
// 接收的同时对数据进行类型限制、默认值指定、必要性设置
props:{
name:{
type:String, // 数据类型
required:true // 该属性是必要的
default:20 // 默认值,不和 required 同时设置
}
}
- 备注
props 是只读的,Vue 底层会检测对props的修改,如果进行了修改,就会发出警告。若确实需要修改,则需要复制 props 的内容到 data中一份,然后修改 data 中的数据。
2.3. mixin
- 把多个组件共用的配置提取成一个混入对象。
- 定义并暴露混入:
export const yytMixin = {
data(){
return { ... }
},
methods:{
...
},
... ...
}
- 使用混入:
import {yytMixin, ...} from '../mixin.js'
// 全局混入
Vue.mixin(yytMixin);
// 局部混入(配置对象,与data、methods等平级)
mixins:['yytMixin']
2.4. 插件
- 用于增强Vue,本质上是包含
install
方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据。 - 定义插件
对象.install = function(Vue, options){
// 添加全局过滤器
Vue.filter(...);
// 添加全局指令
Vue.directive(...);
// 配置全局混入
Vue.mixin(...);
// 添加实例方法
Vue.prorotype.方法名 = function(){...};
Vue.prototype.属性名 = xxx;
};
- 使用插件 main.js
import plugins from './plugins'
Vue.use(plugins)
2.5. scope样式
让样式在局部作用域生效,防止冲突。<style scoped>
3. 练习
- 组件化编码流程
拆分静态组件: 按照功能点拆分,命名不能与html元素冲突;
实现动态组件: 考虑好数据的存放位置,数据是一个组件在用还是多个组件在用 ——
\qquad 一个组件:放在组件自身即可;
\qquad 多个组件:放在它们共同的父组件上(状态提升);
实现交互: 从绑定事件开始。 - 使用props
父组件 ⟶ \longrightarrow ⟶ 子组件 通信
子组件 ⟶ \longrightarrow ⟶ 父组件 通信(需要父组件给子组件传一个函数,然后子组件调用这个函数)
props传过来的若是对象类型的值,那么修改其中的属性值不会报错,但最好不要这样做。 - 使用v-model
不能绑定 props 传过来的值,因为 props 传过来的值不能修改。
4. 浏览器本地存储
- 存储内容大小一般支持 5MB 左右(不同浏览器可能不一样);
- 浏览器通过
Window.sessionStorage
和Window.localStorage
实现本地存储机制; - 相关API
xxxxxStorage.setItem('key','value')
接收一个键和值作为参数,把键值对添加到存储中,如果键名存在,那么更新它对应的值;
xxxxxStorage.getItem('key')
接收一个键名作为参数,返回键对应的值;
xxxxxStorage.removeItem('key')
接收一个键名作为参数,并把该键名从存储中删除;
xxxxxStorage.clear()
清空存储中的所有数据。 - 备注
sessionStorage 存储的内容会随着浏览器窗口关闭而消失;
localStorage 存储的内容需要手动清除才会消失;
xxxxxStorage.getItem('key')
如果对应的value获取不到,那么返回一个null
;
JSON.parse(null)
的结果依然是null
。
4.1. local storeage
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="../imgs/kirlant.ico">
<title>kirlant</title>
<style>
.root{
width: 400px;
border: 2px solid #aaa;
padding: 10px 40px;
}
.title{
font-weight: bold;
text-align: center;
border-bottom: solid 1.5px #aaa;
padding-bottom: 5px;
}
.gap{
padding: 10px 5px;
border-bottom: 1px solid #aaa;
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-content:center;
}
button{
margin: 5px;
width: 160px;
}
</style>
</head>
<body>
<div class="root">
<div class="title">Welcome to Kirlant's Test</div>
<div class="gap">
<button onclick="savaData()">save one data</button>
<button onclick="readData()">read one data</button>
<button onclick="delData()">delete one data</button>
<button onclick="delAllData()">delete all data</button>
</div>
</div>
<script type="text/javascript">
let p = {
name:'kirlant',
age:16
};
let idx = 0;
function savaData(){
localStorage.setItem('person_'+idx,JSON.stringify(p));
++p.age;
++idx;
};
function readData(){
const result = localStorage.getItem('person_2');
console.log(JSON.parse(result));
};
function delData(){
localStorage.removeItem('person_2');
};
function delAllData(){
localStorage.clear();
}
</script>
</body>
</html>
4.2. session storage
function savaData(){
sessionStorage.setItem('person_'+idx,JSON.stringify(p));
++p.age;
++idx;
};
function readData(){
const result = sessionStorage.getItem('person_2');
console.log(JSON.parse(result));
};
function delData(){
sessionStorage.removeItem('person_2');
};
function delAllData(){
sessionStorage.clear();
}
5. 组件自定义事件
- 一种组件间通信的方式,适用于 子组件 ⟶ \longrightarrow ⟶ 父组件;
- 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中);
- 绑定自定义事件:
方式一:在父组件中<sonComponentName @eventName="callbackName" />
或<sonComponentName v-on:eventName="callbackName" />
方式二:在父组件中<sonComponentName ref="refName" /> ... mounted(){ this.$refs.refName.on("eventName", this.callbackName); }
若想让自定义事件只能触发一次,可以使用once
修饰符或$once
方法。 - 触发自定义事件:
this.$emit('eventName', data)
; - 解绑自定义事件:
this.$off('eventName')
; - 组件上可以绑定原生DOM事件,需要使用
native
修饰符; - 通过**
this.$refs.refName.on("eventName", this.callbackName)
**绑定自定义事件时,回调要么配置在methods
中,要么用箭头函数,否则this
指向会出问题。
啾咪
6. 全局事件总线
一种组件间通信的方式,适用于任意组件间通信。
- 安装 (我绑我自己???)
new Vue({
...
beforeCreate(){
Vue.prototype.$bus = this; // 安装全局事件总线,$bus就是当前使用的 vm
}
...
})
- 使用
接收数据:A组件想接收数据,则在A组件中给$bus
绑定自定义事件,事件的回调留在A组件自身;
提供数据:this.$bus.$emit( 'xxx', 数据 )
methods(){
demo(data){...}
},
...
mounted(){
this.$bus.$on('xxx', this.demo);
}
最好在beforeDestroy钩子中用$off
解绑当前组件用到的事件。
7. 消息订阅与发布
一种组件间通信的方式,适用于任意组件间通信。
- 安装:
npm i pubsub-js
- 引入:
import pubsub from pubsub-js
- 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身;
methods(){
demo(data){...};
},
...
mounted(){
this.pid = pubsub.subscribe('xxx', this.demo); // 订阅消息
}
- 提供数据:
pubsub.publish('xxx', 数据)
最好在beforeDestroy钩子中用pubsub.unsubscribe(this.pid)
取消订阅。
8. 动画与过渡
- 作用
在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名。 - 写法
a). 准备样式
\quad 进入:v-enter
起点;v-enter-active
过程中;v-enter-to
终点;
\quad 离开:v-leave
起点;v-leave-active
过程中;v-leave-to
终点;
b). 用<transition>
包裹要过渡的元素,并配置name
属性
备注:若有多个元素需要过渡,则需要使用<transition-group>
,且每个元素都要指定key
值。
<transition name="yyt">
<div class="great" v-show="isShow">太强了!</div>
</transition>
9.配置代理
- 方法一
在vue.config.js
中添加如下配置:devServer: {proxy: 'http://localhost:5000'}
;
优点:配置简单,请求资源时直接发给前端(8080)即可;
缺点:不能配置多个代理,不能灵活控制是否走代理;
工作方式:优先匹配前端资源,只有当请求了前端不存在的资源时,请求才会转发给服务器。 - 方法二
在vue.config.js
配置具体代理规则;
优点:可以配置多个代理,可以灵活控制是否走代理;
缺点:配置略微繁琐,且请求资源时必须加前缀。
devServer: {
proxy: {
'/api': { // 匹配所有以 '/api' 开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
pathRewrite:{'^/api':''},
ws: true, // 用于支持 websocket
changeOrigin: true //控制请求头中的host值
},
'/finish': {
target: 'http://localhost:5001',
pathRewrite:{'^/finish':''},
ws: true,
changeOrigin: true
}
}
}
changeOrigin默认值为true;
设置为 true 时,服务器收到的请求头中的host为 localhost:5000(服务器的端口号);
设置为 false 时,服务器收到的请求头中的host为 localhost:8080(代理服务器实际的端口号)
10. 插槽
作用: 让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件
⟶
\longrightarrow
⟶ 子组件;
分类: 默认插槽,具名插槽,作用域插槽;
使用方式:
- 默认插槽
<!-- 父组件中 -->
<OneCategory>
<div>html结构</div>
</OneCategory>
<!-- 子组件中 -->
<template>
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容</slot>
</div>
</template>
- 具名插槽
<!-- 父组件中 -->
<OneCategory">
<template slot="name1">
<div>html结构</div>
</template>
<template v-slot:name2>
<div>html结构</div>
</template>
</OneCategory>
<!-- 子组件中 -->
<template>
<div>
<!-- 定义插槽 -->
<slot name="name1">插槽默认内容</slot>
<slot name="name2">插槽默认内容</slot>
</div>
</template>
- 作用域插槽
数据在子组件自身,但根据数据生成的结构要由使用它的父组件决定。
<!-- 父组件中 -->
<div class="container">
<!-- 父组件中 -->
<OneCategory title="游戏">
<template slot-scope="data">
<ul><li v-for="(item, index) in data.games" :key="index">{{item}}</li></ul>
</template>
</OneCategory>
<OneCategory title="游戏">
<template slot-scope="data">
<ol><li v-for="(item, index) in data.games" :key="index">{{item}}</li></ol>
</template>
</OneCategory>
<OneCategory title="游戏">
<template slot-scope="data">
<h4 v-for="(item, index) in data.games" :key="index">{{item}}</h4>
</template>
</OneCategory>
</div>
<!-- 子组件中 -->
<template>
<div class="category">
<h3>{{title}}</h3>
<!-- 定义一个插槽,等待组件的使用者进行填充 -->
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name:"OneCategory",
props:['title'],
data(){
return { games:['红色警戒','穿越火线','劲舞团','超级玛丽']}
}
}
</script>