文章目录
一、创建Vue脚手架
1、说明
- Vue脚手架是Vue官方提供的标准化开发工具(开发平台)
- 最新的版本是4.x
- 文档:https://cli.vuejs.org/zh/
2、具体步骤
第一步(仅第一步执行):全局安装@vue/cli
npm install -g @vue/cli
第二步:切换到你要创建项目的目录,然后使用命令创建项目
vue create testProject
第三步:启动项目
npm run serve
备注:
- 如出现下载缓慢,请配置npm淘宝镜像:
npm config set registry https://registry.npm.taobao.org
- Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:
vue inspect > output.js
3、脚手架结构
├── node_modules
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件
4、修改默认配置
- Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:
vue inspect > output.js
,打开的output.js的文件只是抛出展示相关的webpack配置,修改是无效的 - 文件夹的public、文件夹src、文件public/index.html、文件public/favicon.ico、文件src/main.js、文件package.json是不允许更改名称的
- 要改默认配置,需借助vue.config.js文件,同package.json是同级的
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/index/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html',
// 当使用 title 选项时,
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></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',
lintOnSave: false, // 关闭语法检查
}
}
二、render函数
- vue.js与vue.runtime.xxx.js的区别:
- vue.js是完整版的Vue,包含:核心功能+模板解析器
- vue.runtime.xxx.js是运行版的vue,只包含:核心功能,没有模板解析器
- vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数区指定具体的内容
// 引用到的vue是vue.runtime.xxx.js,只包含:核心功能,没有模板解析器
import Vue from 'vue'
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
new Vue({
el: '#root',
// 不能使用template,没有模板解析器
// 需借助render函数,返回要渲染的元素
// render(createElement) {
// // createElement函数是创建虚拟dom节点的,接收两个参数,第一个是标签名,第二个是内容
// // return createElement('h1', '你好啊!')
// return createElement(App)
// }
render: h => h(App)
})
通俗易懂的距离讲解:
装修–铺瓷砖
-
第一种:买瓷砖(Vue核心) + 买工人(模板解析器) ====> 铺好的瓷砖 + 工人
装修完后,获得了铺好的瓷砖,还得一直养着工人
-
第二种:买瓷砖(Vue核心) + 雇工人(模板解析器) ====> 铺好的瓷砖
细节问题:
组件还是有template,借助vue-template-compiler这个依赖库来解析
三、ref属性/props配置/mixin混入
1、ref属性
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button @click="showDom" ref='btn'>点我输出上方dom元素</button>
<School ref="sch"></School>
</div>
</template>
<script>
// 引入School组件
import School from './components/School'
export default {
name: 'App',
data() {
return {
msg: 'hello'
}
},
components: { School },
methods: {
showDom() {
console.log(this.$refs)
console.log(this.$refs.title, this.$refs.btn) // 真实dom元素
console.log(this.$refs.sch) // 获取到School组件实例对象(vc)
}
}
}
</script>
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实dom元素,应用在组件标签上是组件实例对象(vc)
- 使用方式:
- 打标识:
<h1 ref="xxx"></h1>
或<School ref="xxx"></School>
- 获取:
this.$refs.xxx
- 打标识:
2、props配置
<template>
<div>
<h1>{{msg}}</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>性别:{{sex}}</h2>
</div>
</template>
<script>
export default {
name: 'School',
// 简单声明接收
// props: ['name', 'age', 'sex'],
// 接收的同时对数据进行类型限制
// props: {
// name: String,
// age: Number,
// sex: String
// },
// 接收的同时对数据进行类型限制 + 默认值的指定 + 必要性的限制
props: {
name: {
type: String,
required: true // 是否必要
},
age: {
type: Number,
default: 99 // 默认值
},
sex: {
type: String,
required: true
}
},
data() {
return {
msg: 'hello'
}
}
}
</script>
-
简单声明接收:
props: ['name', 'age', 'sex']
-
接收的同时对数据进行类型限制:
props: { name: String, age: Number, sex: String }
-
接收的同时对数据进行类型限制 + 默认值的指定 + 必要性的限制:
props: { name: { type: String, required: true // 是否必要 }, age: { type: Number, default: 99 // 默认值 }, sex: { type: String, required: true } }
-
备注:
props是只读的,Vue底层会监测你对props的修改。如果进行了修改,就会发出警告。若业务需求确实要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
3、mixin混入(混合)
-
功能:可以把多个组件共用的配置提取成一个混入对象
-
使用方式:
-
第一步定义混合:
{ data() {...}, methods: {...}, ... }
-
使用混入,例如:
- 全局混入:
Vue.mixin(xxx)
- 局部混入:
mixins: ['xxx']
- 全局混入:
-
四、插件
plugins.js插件,代码如下所示:
export default {
install(Vue) {
// 全局过滤器
Vue.filter('mySlice', function(value){
return value.slice(0, 4)
})
// 自定义全局指令
Vue.directive('fbind', {
// 指令与元素成功绑定时(一上来)
bind(element, binding) {
element.value = binding.value
},
// 指令所在的元素被插入页面时
inserted(element, binding) {
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
})
// 定义混入
Vue.mixin({
data () {
return {
x: 100
}
}
})
// 给Vue的原型加上方法(vm和vc都能用)
Vue.prototype.hello = () => alert('你好啊')
console.log('这是一个插件', Vue)
}
}
main.js使用插件
import Vue from 'vue'
import App from './App.vue'
// 引入插件
import plugins from './plugins.js'
//关闭Vue的生产提示
Vue.config.productionTip = false
// 应用(使用)插件
Vue.use(plugins)
// 创建vm
new Vue({
el: '#app',
render: h => h(App)
})
- 功能:用于增强vue
- 本质:包含install方法的一个对象,install的第一个参数时Vue,第二个以后的参数时插件使用者传递的数据
- 定义插件:
对象.install = function (Vue, options) { ... }
- 使用插件:
Vue.use()
五、scoped样式
- 作用:让样式在局部生效,阻止冲突
- 写法:
<style scoped></style>
六、本地存储
1、localStorage(本地缓存)
- 添加缓存:
localStorage.setItem(key, value)
,其中key,value都是string类型,非string类型,会去调用toString()方法 - 获取缓存:
localStorage.getItem(key, value)
- 清除缓存:
localStorage.removeItem(key, value)
- 清除全部缓存:
localStorage.clear()
2、sessionStorage(会话缓存)
- 添加缓存:
sessionStorage.setItem(key, value)
,其中key,value都是string类型,非string类型,会去调用toString()方法 - 获取缓存:
sessionStorage.getItem(key, value)
- 清除缓存:
sessionStorage.removeItem(key, value)
- 清除全部缓存:
sessionStorage.clear()
3、区别
localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。
sessionStorage 数据保存在当前会话中,该数据对象临时保存同一窗口 (或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。
4、总结
-
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
-
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制
-
相关API:
-
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
-
xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值
-
xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除
-
xxxxxStorage.clear();
该方法会清空存储中的所有数据
-
-
备注:
- SessionStorage存储的内容会随着浏览器窗口关闭而消失
- LocalStorage存储的内容,需要手动清除才会消失
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是nullJSON.parse(null)
的结果依然是null
七、事件
1、组件的自定义事件
// 父组件
// 第一种写法,使用@或v-on
// <School @test="test" @demo="demo"/>
// 第二种写法,使用ref
// <School ref="school" @click.native="show"/>
// ===> this.$refs.school.$on('test', 需触发的函数)
// 需触发的函数为function(){}时,this指向所触发的子组件
// 需触发的函数为箭头函数或this.函数名时,this指向父组件
// ===> 想触发原生DOM事件,需加修饰符native,不然会被认为是自定义事件
// 触发School组件实例身上的test事件
this.$emit('test', this, 1, 2)
this.$emit('demo')
// 解绑自定义事件
this.$off('test')
// 解绑多个自定义事件
this.$off(['test', 'demo'])
// 解绑所有自定义事件
this.$off()
// 销毁当前Student组件的实例
this.$destroy() // 销毁后所有Student实例的自定义事件全都不奏效
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
-
绑定自定义事件:
-
第一种方式,在父组件中:
<Demo @atguigu="test"/>
或<Demo v-on:atguigu="test"/>
-
第二种方式,在父组件中:
<Demo ref="demo"/> ...... mounted(){ this.$refs.xxx.$on('test',this.test) }
-
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法 -
触发自定义事件:
this.$emit('test',数据)
-
解绑自定义事件:
this.$off('tese')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符 -
注意:通过
this.$refs.xxx.$on('test',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
2、全局事件线
任意组件间通信
-
一种组件间通信的方式,适用于任意组件间通信
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... })
-
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){ demo(data){......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) }
-
提供数据:
this.$bus.$emit('xxxx',数据)
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
-
八、消息订阅与发布
1、消息订阅与发布
-
一种组件间的通信方式,适用于任意组件的通信
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件订阅消息,订阅的回调留在A组件
<script> export default { data() { return {} }, methods: { demo(msgName, data) {......} }, mounted() { this.pub = pubsub.subscribe('xxx', this.demo) // 订阅消息 } } </script>
-
提供数据:
pubsub.publish('demo', 数据)
-
最好在beforeDestroy钩子中,用
pubsub.unsubscribe(pub)
去取消订阅
2、_nextTick
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
九、Vue封装的过度与动画
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<!-- 单个元素过度 -->
<!--
<transition name="hello" appear>
<h1 v-show="isShow">你好啊!</h1>
</transition>
-->
<!-- 多个元素过度 -->
<!--
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
-->
<!-- 借助animate.css库 -->
<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;
/* transition: 0.5s linear; */
}
/* 方式一 */
/* 进入的起点、离开的终点 */
.hello-enter,.hello-enter-to{
transform: translateX(-100%);
}
/* 进入的终点、离开的起点 */
.hello-enter-to,.hello-enter{
transform: translateX(0px);
}
/* 进入的过程、离开的过程 */
.hello-enter-active,.hello-leave-active{
transition: 0.5s linear;
}
/* 方式二 */
.hello-enter-active{
animation: test 0.5s linear;
}
.hello-leave-active{
animation: test 0.5s linear;
}
@keyframes test {
from {
transform: translateX(-100%)
}
to {
transform: translateX(0px)
}
}
</style>
-
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
-
写法:
- 准备好样式:
- 元素进入的样式:
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
- 元素离开的样式:
- v-leave:离开的起点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
- 元素进入的样式:
- 准备好样式:
-
使用
<transition>
包裹要过度的元素,并配置name属性:<transition name="hello"> <h1 v-show="isShow">你好啊!</h1> </transition>
-
备注:若有多个元素需要过度,则需要使用:
<transition-group>
,且每个元素都要指定key
值。
十、配置代理
1、方法一
在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
2、方法二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
十一、vue-resource
请求方法:xhr、jQuery、axios、fetch、vue-resource
vue-resource刚开始是vue团队维护,后面交给其他团队,1.0用得比较多,vue的插件,需了解下
1、安装
npm i vue-resouorce
2、引用插件
import vueResource from 'vue-resource'
3、使用插件
Vue.use(vueResource)
Vue实例vm和组件实例vc,都会多出$http对象,同axios的使用是一样的
十二、插槽
插槽就是子组件提供给父组件的一个占位符,父组件可以在这个占位符填充任何模板代码,如HTML、组件等
插槽显不显示、怎么显示由父组件决定
插槽在哪里显示由子组件决定
1、默认插槽
没有名字的插槽,子组件未定义名字的插槽,父组件把未定义的插槽内容放到默认插槽
子组件child
<template>
<div>
<h3>默认插槽</h3>
<slot></slot>
</div>
</template>
父组件
<template>
<div>
<child>填充默认的插槽内容</child>
<!-- vue2.6的新写法,必须搭配template标签使用,可简写为# -->
<child>
<template v-slot:default>
<h4>默认插槽-新写法</h4>
<div>填充默认的插槽内容</div>
</template>
</child>
<child>
<template #default>
<h4>默认插槽-新写法</h4>
<div>填充默认的插槽内容</div>
</template>
</child>
</div>
</template>
2、具名插槽
给插槽取个名字,一个子组件可以放多个插槽,而且放在不同的地方;而父组件可以根据插槽的名字,把内容填充到对应的插槽
子组件child
<template>
<div>
<slot name='header'></slot>
<h3>具名插槽</h3>
<slot name='footer'></slot>
</div>
</template>
父组件
<template>
<div>
<child>
<div slot="header">插槽头部内容</div>
<div slot="footer">插槽底部内容</div>
</child>
<child>
<template slot="header">
<div>插槽头部内容</div>
</template>
<template slot="footer">
<div>插槽底部内容</div>
</template>
</child>
<!-- vue2.6的新写法,必须搭配template标签使用,可简写为# -->
<child>
<template v-slot:header>
<h4>具名插槽-新写法</h4>
<div>插槽头部内容</div>
</template>
<template #footer>
<h4>具名插槽-新写法</h4>
<div>插槽底部内容</div>
</template>
</child>
</div>
</template>
3、作用域插槽
其实就是带数据的插槽,子组件提供给父组件的参数,该参数仅限于插槽使用
子组件child
<template>
<div>
<slot name='header' :dd='test' msg='hello'></slot>
<h3>作用域插槽</h3>
<slot :dd='test' msg='hello'></slot>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
test: {
name: 'cyr',
age: 18
}
}
}
}
</script>
父组件
父组件
<template>
<div>
<child>
<template scope="{dd}">
<h3>默认插槽</h3>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
<child>
<template slot-scope="{dd}">
<h3>默认插槽</h3>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
<!-- vue2.6的新写法 -->
<child>
<template v-slot:default="{dd}">
<h4>默认插槽-新写法</h4>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
<child>
<template #default="{dd}">
<h4>默认插槽-新写法</h4>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
<child>
<template v-slot:header="{dd}">
<h4>具名插槽-底部-新写法</h4>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
<child>
<template #header="{dd}">
<h4>具名插槽-底部-新写法</h4>
<div>姓名:{{dd.name}}</div>
<div>年龄:{{dd.age}}</div>
</template>
</child>
</div>
</template>