Vue笔记_03_使用Vue脚手架
第三章 使用Vue脚手架
初始化脚手架
cli(command line interface)
命令行接口工具
说明
- Vue脚手架是Vue官方提供的标准化开发工具(开发平台)
- 文档:https://cli.vuejs.org/zh/
具体步骤
-
全局安装
@Vue/cli
(仅第一次执行)npm install -g @vue/cli
-
切换到你要创建项目的目录,然后使用命令创建项目
vue create xxx
-
启动项目
npm run serve
备注:
-
如出现下载缓慢请配置
npm
淘宝镜像:npm config set registry https://registry.npm.taobao.org
-
Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:
vue inspect > output.js
,output.js
只能看不能修改
项目文件分析
pack-lock-json
版本锁定配置文件,用来锁定项目依赖包的版本
assets文件夹
静态资源文件夹,用来存放一些图片,音频等
components文件夹
组件文件夹,用来存放组件(.vue文件)的文件夹
main.js
/*
该文件是整个项目的入口文件
*/
// 引入Vue
import Vue from 'vue'
// 引入App组件,它是所有组件的父组件
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false
// 创建Vue的实例对象----vm
new Vue({
// 渲染结果
render: h => h(App),
}).$mount('#app')
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<!-- 针对IE浏览器的一个特殊配置,含义是告诉IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 开启移动端的理想视口 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 配置页签的图标 -->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 配置网页的标题 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<!-- 当浏览器不支持js是,noscript中的东西就会被执行 -->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!-- 容器 -->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
main.js里面的render
render(createElement) {
return createElement('h1', '你好呀')
}
// 由于没有用到this,所以可以写成箭头函数
render:(createElement)=> {
return createElement('h1', '你好呀')
}
// 箭头函数由于只有一个参数,所以可以省略括号
render:createElement=> {
return createElement('h1', '你好呀')
}
// 由于箭头函数里面没有语句,只return了一个createElement('h1', '你好呀'),所以可以简写为
render:creatElement=>createElement('h1', '你好呀')
关于不同版本的Vue
- vue.js与vue.runtime.xxx.js的区别
- vue.js是完整版的Vue,包含:核心功能和模板解析器
- vue.rintime.xxx.js是运行版的Vue,只包含:核心功能,没有模板解析器
- 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到createElement函数去指定具体内容
脚手架的默认配置
不能改的配置
- public文件夹
- favicon.ico文件
- index.html文件
- src文件夹
- main.js文件
调整默认配置时
查文档!!!
写个vue.config.js文件修改默认配置,vue.config.js所在位置应该与package.json同层
修改默认入口
module.exports = {
pages: {
index: {
entry: '入口文件地址'
}
}
}
关闭语法检查
module.exports = {
lintOnSave: false
}
一些特殊的标签属性和配置项
ref属性
- 被用来给元素或子组件注册引用信息(id的替代者)
- 应用在html标签上获取的是真实的DOM元素,应用在组件标签上的是组件实例对象(vc)
获取的时候:
<template>
<div>
<h1 v-text="msg" ref="title"></h1>
<button ref='btn' @click="showDOM">点我输出上方的DOM元素</button>
<School ref="sch"></School>
</div>
</template>
<script>
import School from './components/School'
export default {
name:'App',
components: {School},
data() {
return {
msg:'欢迎学习Vue'
}
},
methods: {
showDOM() {
console.log(this.$refs.title) // 真实DOM元素
console.log(this.$refs.btn) // 真实DOM元素
console.log(this.$refs.sch) // School组件的实例对象
}
}
}
</script>
props配置项
功能:让组件接收外部传来的数据
-
传递数据
<Demo name="xxx"></Demo>
-
接收数据
-
第一种方式(只接收)
<script> export default { name:'Student', props['name'] } </script>
-
第二种方式(限制类型)
<script> export default { name:'Student', props: { name: String } } </script>
-
第三种方式(限制类型,限制必要性,指定默认值)
<script> export default { name:'Student', props: { name: { type:String, // 类型 required:true, // 必要性 default:'老王' // 默认值 } } } </script>
备注:
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务中需求确实需要修改,那么请赋值一份props的内容到data中一份,然后去修改data中的数据
<script> export default { name:'Student', data() { return { MyName:n } }, props: { name:{ type:String, required:true } } } </script>
-
mixin混入
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
// 第一步定义混合
{
data() {},
methods() {}
}
// 第二步使用混入
1. 全局混入:Vue.mixin(xxx)
2. 局部混入:mixins:['xxx']
在mixin文件里:
export mixin = {
data() {
return {
name:'GZU'
}
},
methods: {
showName() {
alert(this.name)
}
}
}
在Vue文件里
<script>
// 局部混入
import {mixin} from 'xxxx'
export default {
name:'School',
mixins:['mixin'] // 局部混入
}
</script>
<script>
// 全局混入
import {mixin} from 'xxxx'
Vue.mixin(mixin) // 全局混入
export default {
name:'School',
}
</script>
插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以及以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue, options) {
// 1. 添加全局过滤器
Vue.filter(...)
// 2. 添加全局指令
Vue.directive(...)
// 3. 配置全局混入
Vue.mixin(...)
// 4. 添加实例方法
Vue.prototype.$myMethod = function() {}
}
使用插件:
Vue.use()
在plugins文件里:
export default {
install() {
// 1. 添加全局过滤器
Vue.filter(...)
// 2. 添加全局指令
Vue.directive(...)
// 3. 配置全局混入
Vue.mixin(...)
// 4. 添加实例方法
Vue.prototype.$myMethod = function() {}
}
}
在main.js文件里
// 引入Vuw
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 引入插件
import plugins from './plugins'
// 关闭Vue的生产提示
Vue.productionTip = false
// 应用插件
Vue.use(plugins)
// 创建vm
new Vue({
render: h=>h(App),
}).$mount('#app')
写Style标签的一个技巧
// 加上scoped可以保证样式表不会冲突
// 但是在App文件里面一般不添加这个
<style scoped></style>
// 可以用less书写
// 用less书写的时候要安装less-loader
<style lang="less"></style>
// 安装less-loader的时候报错是因为版本不兼容的问题
// node
// 查看版本
npm view less-loader version
// 安装7版本,默认是7版本中的最新版本
npm i less-loader@7
Todo-list案例
组件化编码流程(通用)
- 实现静态组件:抽取组件,使用组件实现静态效果
- 展示动态数据:
- 数据的类型,名称是什么?
- 数据保存在那个组件?
- 交互—从绑定事件监听开始
数据在哪里,操作就在哪里
- 组件化编码流程:
- 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
- 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
- 一个组件在用:放在自身即可
- 一些组件在用:放在他们共同的父组件上(状态提升)
- 实现交互:从绑定事件开始
- props适用于:
- 父组件==>子组件通信
- 子组件==>父组件通信(要求父先给子一个函数)
- 使用
v-model
时要切记:v-model
绑定的值不能是props传过来的值,因为props是不可以修改的! - props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这么做
浏览器本地存储
- 存储内容大小一般支持5MB左右(不同浏览器可能害不一样)
- 浏览器端通过
Window.localStorage
和Window.sessionStorage
属性来实现本地存储机制 - 相关API:见下方的代码
- 备注:
sessionStorage
存储的内容会随着浏览器窗口关闭而消失localStorage
存储的内容,需要手动清除才会消失localStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem获取的返回值是nullJSON.parse(null)
的结果依旧是null
在localStorage.html文件里:
<!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">
<title>localStorage</title>
</head>
<body>
<h2>localStorage</h2>
<button onclick="saveData()">点我保存数据</button>
<button onclick="readData()">点我读取数据</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
let p = {name:'张三', age:18}
// 保存数据到本地
function saveData() {
// 如果传入的第二个参数不是字符串,会默认使用`toString`方法
localStorage.setItem('msg', 'hello')
localStorage.setItem('person', JSON.stringify(p))
}
// 读取本地保存的数据
function readData() {
console.log(localStorage.getItem('msg'))
const result = localStorage.getItem('person')
// JSON.parse()方法将数据转换成javascript对象
console.log(JSON.parse(result))
}
// 删除本地的数据
function deleteData() {
localStorage.removeItem('msg')
}
// 清空保存在本地的数据
function deleteAllData() {
localStorage.clear()
}
</script>
</body>
</html>
在sessionStorage.html文件里:
<!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">
<title>sessionStorage</title>
</head>
<body>
<h2>sessionStorage</h2>
<button onclick="saveData()">点我保存数据</button>
<button onclick="readData()">点我读取数据</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我清空数据</button>
<script>
let p = {name:'张三', age:18}
// 保存数据到本地
function saveData() {
// 如果传入的第二个参数不是字符串,会默认使用`toString`方法
sessionStorage.setItem('msg', 'hello')
sessionStorage.setItem('person', JSON.stringify(p))
}
// 读取本地保存的数据
function readData() {
console.log(sessionStorage.getItem('msg'))
const result = sessionStorage.getItem('person')
// JSON.parse()方法将数据转换成javascript对象
console.log(JSON.parse(result))
}
// 删除本地的数据
function deleteData() {
sessionStorage.removeItem('msg')
}
// 清空保存在本地的数据
function deleteAllData() {
sessionStorage.clear()
}
</script>
</body>
</html>
组件自定义事件
绑定事件
从School组件中传数据给App组件:
在App组件中:
<template>
<div id="app">
<!-- 这是第一种绑定写法 -->
<!-- 当School组件中触发mine事件的时候,执行回调函数getSchoolName -->
<School v-on:mine="getSchoolName"/>
<!-- 给Student绑定一个ref属性 -->
<Student ref="student"/>
</div>
</template>
<script>
import School from './components/School.vue'
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
School,
Student
},
methods: {
getSchoolName(name) {
console.log(name)
},
getStudentName(name) {
console.log(name)
}
},
mounted() {
// 这是第二种绑定写法
// 给拥有student这个ref属性的组件添加一个自定义属性,当自定义属性触发的时候执行getStudentName这个回调函数
this.$refs.student.$on('mine', this.getStudentName)
},
}
</script>
在School组件中:
<template>
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<!-- 点点击的时候,执行sendSchoolName回调函数 -->
<button @click="sendSchoolName">点击发送学校名</button>
</div>
</template>
<script>
export default {
name:'School',
data() {
return {
schoolName:'Gzu',
address:'GuiYang'
}
},
methods: {
sendSchoolName() {
// 触发组件上的mine自定义事件,将this.schoolName作为参数传回去
this.$emit('mine', this.schoolName)
}
},
}
</script>
在Student组件中:
<template>
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName">点击发送学生名</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
studentName:'SoLi',
age:17,
sex:'remale'
}
},
methods: {
sendStudentName() {
// 触发组件上的mine自定义事件,将this.studentName作为参数传回去
this.$emit('mine', this.studentName)
}
},
}
</script>
<style>
</style>
一个扩展知识:
function fn(value, ...arr) {} // 这种函数的定义,在调用的时候,如果传的参数有多个,除了第一个参数传给value之外,其他参数传递给arr作为数组成员
- 组件上也可以使用原生DOM事件,需要使用
native
修饰符 21
解绑事件
在Student组件文件中
<template>
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName">点击发送学生名</button>
<button @click="unbind">点击解绑mine事件</button>
<button @click="death">点击销毁student实例(vc)</button>
</div>
</template>
<script>
export default {
name:'Student',
data() {
return {
studentName:'SoLi',
age:17,
sex:'remale'
}
},
methods: {
sendStudentName() {
// 触发组件上的mine自定义事件,将this.studentName作为参数传回去
this.$emit('mine', this.studentName)
},
unbind() {
this.$off('mine') // 解绑一个自定义事件
this.$off(['mine', 'xxx', 'xxx']) // 解绑多个自定义事件
this.$off() // 解绑全部自定义事件
},
death() {
this.$destroy() // 销毁了当前的Student组件的实例,销毁后所有Student实例的自定义事件全都无效了
}
},
}
</script>
全局事件总线
<script>
import Search from './components/Search.vue'
import UsersList from './components/UsersList.vue'
import Vue from 'vue'
export default {
name: 'App',
components: {
Search,
UsersList
},
// 注册全局事件总线
beforeCreate() {
Vue.prototype.$bus = this
}
}
</script>
消息订阅与发布
-
一种组件间通信的方式,适用于任意组件间通信
-
使用步骤:
-
安装pubsub:
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(pid)
去<span style="color:red">取消订阅</span>
-
$nextTick
(常用)
-
语法:
this.$nextTick(回调函数)
-
作用:在下一次DOM更新结束后执行其指定的回调
-
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
this.$nextTick(function() { this.$refs.inputTitle.focus() // 获取焦点 })
过渡与动画
动画效果
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<!-- name属性是为了区分各个动画 appear是为了在网页刷新的时候就启动动画效果 -->
<!-- 将需要添加动画的地方添加transition标签 -->
<transition name="hello" appear>
<h1 v-show="isShow">你好,世界</h1>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
isShow: true,
}
},
}
</script>
<style scoped>
h1 {
background-color: blueviolet;
color: #fff;
padding: 15px;
}
.hello-enter-active {
animation: move 1s linear;
}
.hello-leave-active {
animation: move 1s linear reverse;
}
@keyframes move {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0px);
}
}
</style>
过渡效果
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<!-- name属性是为了区分各个动画 appear是为了在网页刷新的时候就启动动画效果 -->
<!-- 将需要添加动画的地方添加transition标签 -->
<transition name="hello" appear>
<h1 v-show="isShow">你好,世界</h1>
</transition>
<!-- 多个元素需要添加动画,需要使用下面这个标签包裹,需要在元素上使用key值 -->
<!--
<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 {
data() {
return {
isShow: true,
}
},
}
</script>
<style scoped>
h1 {
background-color: blueviolet;
color: #fff;
padding: 15px;
/* transition: 0.5s linear; */
}
/* 过渡效果也可以这样写(推荐写法) */
.hello-enter-active, .hello-leave-active {
transition: 0.5s linear;
}
/* 进入的起点 */
.hello-enter {
transform: translateX(-100%);
}
/* 进入的终点 */
.hello-enter-to {
transform: translateX(0);
}
/* 离开的起点 */
.hello-leave {
transform: translateX(0);
}
/* 离开的终点 */
.hello-leave-to {
transform: translateX(-100%);
}
/* 可以优化一下,进入的起点和离开的终点,进入的终点和离开的起点都是一样的 */
</style>
集成第三方动画
Animate.css
Vue封装的过渡与动画
-
作用:在插入,更新或移除DOM元素时,在合适的时候给元素添加样式类名
-
写法:
-
准备好样式:
- 进入的样式:
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
- 离开的样式:
- v-leave:离开的起点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
- 进入的样式:
-
使用
<transition>
包裹要过渡的元素,并配置name属性:<transition> <h1 v-show="isShow"> 你好呀! </h1> </transition>
-
备注:若有多个元素需要过渡,则需要使用
<transition-group>
,且每个元素都要指定key
值
-