# Vue(4)
# 13. vue-cli
# 13.1 vue项目的安装
- 安装vue-cli:
npm install -g @vue-cli
- 在指定的目录下创建项目:
项目名称(英文的,最好别带空格)
- 方向键控制选中的文字,建议选第三个(Manually select features),确认敲回车
- 选择需要装的功能,按空格进行选择或取消,括号里有
*
表示安装此项
选项名 | 作用 |
---|---|
Choose Vue version | 询问安装那个版本的Vue |
Babel | 解决JS兼容性**(一定要装)** |
TypeScript | TS,比JS更强大的脚本语言 |
Progressive Web App (PWA) Support | 渐进式的Web框架 |
Router | 路由 |
Vuex | 状态管理 |
CSS Pre-processors | CSS预处理器 |
Linter 1 Formatter | 代码规范检验 |
Unit Testing | 单元测试 |
E2E Testing | 端到端测试 |
选项配置
- Where do you prefer placing config for Babel, ESLint, etc. ?
- In dedicated config files(✔️)
- In package. json
- Save this as a preset for future projects? (y/N)
- 一般选
y
表示保存选择过的选项,下一次如果用相同的配置的话选择起来会快一点
- 最后进行安装,得到如下结果:
added 1241 packages in 40s
13 packages are looking for funding
run `npm fund` for details
🚀 Invoking generators...
📦 Installing additional dependencies...
added 15 packages in 3s
13 packages are looking for funding
run `npm fund` for details
⚓ Running completion hooks...
📄 Generating README.md...
🎉 Successfully created project demo1.
👉 Get started with the following commands:
$ cd demo1
$ npm run serve
- 打开目录的终端界面并运行指令
cd 项目名称
和npm run serve
文件结构目录
node_modules
: 项目依赖,用npm安装的包都在这里放着src
: 源码assets
: 存放项目中用到的静态资源文件,例如: css 样式表、图片资源components
: 程序员封装的、可复用的组件,都要放到components 目录下- main.js 是项目的入口文件。整个项目的运行,要先执行main.js
App.vue
: 项目的根组件
# 13.2 vue项目的运行与使用
- 在工程化的项目中,vue 要做的事情很单纯——通过
main.js
把App.vue
渲染到index.html
的指定区域中。- App.vue用来编写待渲染的模板结构
- index.html中需要预留一个el区域
- main.js把App.vue渲染到了index.html所预留的区域中
main.js:
//导入vue这个包,得到Vue构造函数
import Vue from 'vue'
//导入App.vue根组件,将来要把App.vue中的模板结构,渲染到HTML页面中
import App from './App.vue'
Vue.config.productionTip = false
//创建Vue的实例对象
new Vue({
el: '#app',
//把render函数指定的组件渲染到HTML页面中
render: h => h(App)
})
// Vue实例的$mount() 方法,作用和el属性完全一样!
# 14. Vue组件
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注
册类型:全局注册和局部注册。
- vue是一个支持组件化开发的前端框架。
- vue中规定组件的后缀名是
.vue
。之前接触到的App.vue文件本质上就是一个vue的组件
- vue组件的三个组成部分
template
-> 组件的模板结构script
-> 组件的JavaScript行为style
-> 组件的样式
- 在components目录下新建自定义组件
// test.vue
<template>
<!-- template中只能包含一个根节点 -->
<div class="test">
<div class="a">
{{ str }}
<p>111111</p>
</div>
<button @click="changeText">改变文字</button>
</div>
</template>
<script>
// 默认导出,这是固定写法
// 注意: .vue组件中的data不能像之前一样,不能指向对象
// 注意: 组件中的data必须是一个函数
export default {
data() {
// 这个return出去的{ }中,可以定义数据
return {
str: "hello vue.js",
};
},
methods: {
changeText() {
this.str = "hello world";
},
},
};
</script>
<style lang="less">
/* 用less的话得加上lang="less" */
.test {
background-color: orange;
.a {
color: red;
p {
color: purple;
}
}
}
</style>
- 在App.vue文件中引用并使用
<template>
<div id="app">
<h1>这是根组件</h1>
<!-- 步骤3: 以标签的形式使用刚才注册的组件 -->
<Test></Test>
</div>
</template>
<script>
// 步骤1: 使用import语法导入需要的组件
import Test from '@/components/Test.vue'
export default {
// 步骤2: 使用components节点注册组件,一般组件名首字母大写来与原生组件区别
components: {
Test
}
}
</script>
<style lang="less">
#app {
font-family: Avenir,Helvetica,Arial,sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
background-color: #ccc;
}
</style>
- 路径提示插件安装
VScode插件名:
Path Autocomplete
,作者名:Mihai Vilcu
- 在settings.json文件中加入如下代码:
// import导入文件时是否携带文件的扩展名 "path-autocomplete.extensionOnImport": true, // 配置@的路径提示 "path-autocomplete.pathMappings": { "@": "${folder}/src" }
还有三个插件【推荐】:
Vetur
、Auto Close Tag
和Vue 3 Snippets
# 14.1 vue 全局组件注册
在vue项目的main.js入口文件中,通过Vue.component(...)
方法注册全局组件
// 导入需要全局注册的组件
import Count from '@/components/Count.vue'
// 参数1: 字符串格式,表示组件的注册名称
// 参数2: 需要被全局注册的那个文件
Vue.component('MyCount',Count)
# 14.2 组件的复用性
props
是组件的自定义属性,在封装通用组件的时候,合理地使用props可以极大的提高组件的复用性!
<!-- 封装的组件的vue文件 -->
<script>
export default {
// props是”自定义属性”,允许使用者通过自定义属性,为当前组件指定初始值
// 注意: props是只读的,不要直接修改props的值,否则终端会报错!
props: {
init: {
// 设置自定义属性的默认值
default: 0,
// 指定值的类型必须是Number数字
type: Number,
// 必填属性
required: true
}
},
data() {
return {
// 要想修改props的值,可以把props的值转存到data中,因为data中的数据都是可读可写的!
count: this.init
}
}
}
</script>
<!-- 使用自定义组件的vue文件 -->
<MyCount :init="6"></MyCount>
# 14.3 组件样式的冲突
默认情况下,写在**.vue组件中的样式会全局生效**,因此很容易造成多个组件之间的样式冲突问题。
导致组件之间样式冲突的根本原因是:
- 单页面应用程序中,所有组件的DOM结构,都是基于唯一的index.html页面进行呈现的
- 每个组件中的样式,都会影响整个index.html页面中的DOM元素
使用属性选择器,在组件内的每个标签都加上一个固定的属性或者在style加scoped属性:
<style lang="less" scoped>...</style>
# 14.4修改子组件样式
/deep/
# 14.5 生命周期和生命周期函数
- 生命周期(Life Cycle)是指一个组件从创建->运行->销毁的整个阶段,强调的是一一个时间段。
- 生命周期函数:是由vue框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。
生命周期
- 组件创建阶段: beforeCreate、created、beforeMount、mounted
- 组件运行阶段: beforeUpdate、updated
- 组件销毁阶段: beforeDestroy、destroyed
beforeCreate
:组件的props/data/methods尚未被创建,都处于不可用状态。
初始化事件和生命周期函数
created
:组件的porps/data/methods已创建好,都处于可用的状态。但是组件的模板结构尚未生成!
初始化props、 data、 methods。created生命周期函数,非常常用。经常在它里面,调用methods 中的方法,请求服务器的数据。并且,把请求到的数据,转存到data中,供template模板渲染的时候使用!
基于数据和模板在内存中编译生成HTML结构
beforeMount
:将要把内存中编译好的HTML结构渲染到浏览器中。此时浏览器中还没有当前组件的DOM结构。用内存中编译生成的HTML结构,替换掉el属性指定的DOM元素。
mounted
:已经把内存中的HTML结构,成功的渲染到了浏览器之中。此时浏览器中已然包含了当前组件的DOM结构。
如果想操作DOM,最早只能在mounted进行
beforeUpdate
:将要根据变化过后、最新的数据重新渲染组件的模板结构。根据最新的数据,重新渲染组件的DOM结构
updated
:已经根据最新的数据,完成了组件DOM结构的重新渲染。
第7,8,9步是循环的,如果有改变数据的话。因此当数据变化之后,为了能够操作到最新的DOM结构,必须把代码写到updated生命周期函数中。
beforeDestroy
:将要销毁此组件,此时尚未销毁,组件还处于正常工作的状态。- 销毁当前组件的数据侦听器、子组件、事件监听
destroyed
:组件已经被销毁,此组件在浏览器中对应的DOM结构已被完全移除!
# 14.6 父组件到子组件的传值
父组件向子组件共享数据需要使用自定义属性。示例代码如下:
<template>
<div>
<MyCount :init="myCountNum"></MyCount>
</div>
</template>
<script>
export default {
data() {
return {
myCountNum: 999
};
}
};
</script>
# 14.7 子组件到父组件到传值
子组件向父组件共享数据使用自定义事件,示例代码如下:
<!-- 子组件MyCount -->
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
add(){
this.count++;
this.$emit('numchange',this.count);
}
}
}
</script>
<!-- 父组件 -->
<template>
<div>
{{ nowCnt }}
<MyCount :init="myCountNum" @numchange="getCount"></MyCount>
</div>
</template>
<script>
export default {
data() {
return {
nowCnt: 0
}
},
methods: {
getCount(val){
this.nowCnt = val;
}
},
};
</script>
# 14.8 兄弟组件之间的传值
在vue2.x
中,兄弟组件之间数据共享的方案是EventBus。
# 14.8.1 EventBus的使用步骤
- 在components文件下创建
eventBus.js
模块,并向外共享一个 Vue的实例对象 - 在数据发送方,调用bus. $emit('事件名称',要发送的数据)方法触发自定义事件
- 在数据接收方,调用bus.$on('事件名称',事件处理函数)方法注册一个自定义事件
# 14.8.2 EventBus使用示例
- 兄弟组件A(发送方)
<script>
// 1. 导入eventBus.js模块
import bus from './eventBus.js'
export default {
data() {
return {
msg: "hello vue.js"
}
},
mathods: {
sendMsg() {
// 2. 通过eventBus来发送数据
bus.$emit("share", this.msg)
}
}
}
</script>
- 兄弟组件B(接受方)
<script>
// 1. 导入eventBus.js模块
import bus from './eventBus.js'
export default {
data() {
return {
msgfromLeft: ""
}
},
created() {
// 为bus绑定自定义事件
bus.$on("share", val => {
this.msgfromLeft = val
})
}
}
</script>
- eventBue.js
import Vue from 'vue'
export default new Vue()
# 14.9 ref引用
# 14.9.1 什么是ref引用
ref用来辅助开发者在不依赖于jQuery的情况下,获取DOM元素或组件的引用。 每个vue的组件实例上,都包含一个$refs对象,里面存储着对应的DOM元素或组件的引用。
# 14.9.2 ref的使用
- 在想要操作的DOM上设置ref属性
<p ref="count1">当前值为: {{ count }}</p>
<!-- <hr ref="count1" /> -->
注意事项
默认情况下,组件的$refs指向一个空对象。
- 但是要注意ref属性名的值别冲突,如果冲突了,后面的元素会覆盖之前的元素,如上述代码所示,如果取消第二行注释,那么将获取到
元素 - 也可以在Vue组件上加上ref属性,那么就可以获得组件元素
- 获取DOM
console.log(this.$refs.count1)
# 14.9.3 $nextTick
组件的$nextTick(cb)
会把cb回调推迟到下一个DOM更新周期之后执行。通俗的理解是: 等组件的DOM更新完成之后,再执行cb回调函数。从而能保证cb回调函数可以操作到最新的DOM元素。
# 14.10 动态展示组件
实现此功能需要用到component组件
# 14.10.1 代码示例
<!-- 通过is属性来动态绑定想要展示的组件的名字,切换后之前的组件会被销毁 -->
<template>
<div id="app">
<!-- 1. component标签是vue内置的,作用: 组件的占位符 -->
<!-- 2. is属性的值表示要渲染的组件的名字-->
<component :is="TagName"></component>
<button @click="changeTag">改变组件</button>
</div>
</template>
<script>
import Test from '@/components/test.vue'
import MyCount from '@/components/myCount.vue'
export default {
data(){
return {
TagName: 'Test'
}
},
components: {
Test,
MyCount
},
methods: {
changeTag(){
this.TagName = this.TagName === 'Test' ? 'MyCount' : 'Test';
}
}
}
</script>
更改is
属性的值会导致原来的组件会被销毁,新的组件会被创建,可以在组件中加入如下代码查看:
<script>
export default {
name: "MyCount",
data() {
return {
count: 0
}
},
created() {
console.log("MyCount组件被创建了");
},
destroyed() {
console.log("MyCount组件被销毁了");
}
}
</script>
# 14.10.2 keep-alive的使用
为了解决上面提到的问题,可以使用keep-alive组件
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png"><br>
<button @click="name='Left'">显示Left组件</button>
<button @click="name='Right'">显示Right组件</button>
<hr>
<div class="componentsBox">
<!-- keep-alive可以把内部的组件进行缓存,而不是销毁组件 -->
<keep-alive>
<component :is="name"></component>
</keep-alive>
</div>
</div>
</template>
打开Vue DevTools工具查看,未显示的组件后会有inactive
标记
keep-alive对应的生命周期函数
- 当组件被缓存时,会自动触发组件的deactivated生命周期函数。
- 当组件被激活时,会自动触发组件的activated生命周期函数。
代码如下:
<script>
export default {
...,
// 先于activated执行
created() {
console.log("组件被创建了");
},
destroyed() {
console.log("组件被销毁了");
},
activated() {
console.log("组件被激活了");
},
deactivated() {
console.log("组件被缓存了");
},
...
}
</script>
include
属性指定要缓存的组件include="MyCount,Test"
exclude
属性同理,但是include和exclude不能同时使用
引号里写的是每个组件的name属性名称,示例如下:
<script>
export default {
//当提供了name 属性之后,组件的名称,就是name 属性的值
//对比:
// 1.组件的“注册名称”的主要应用场景是以标签的形式,把注册好的组件,渲染和使用到页面结构之中
// 2.组件声明时候的“name” 名称的主要应用场景: 结合<keep-alive>标签实现组件缓存功能;以及在调试工具中看到组件的name名称
name: "MyCount",
...
}
</script>
现在vue3里需要按如下方式进行:
<router-view v-slot="{ Component }">
<keep-alive include="Axios">
<component :is="Component" />
</keep-alive>
</router-view>
# 14.11 插槽(slot)的使用
<template>
<div>
<p>这是Count组件</p>
<p ref="count1">当前值为: {{ count }}</p>
<button @click="add">+1</button>
<!-- 声明一个插槽区域-->
<!-- vue 官方规定:每一个slot插槽,都要有一个name名称-->
<!-- 如果省略了slot 的name属性,则有一个默认名称叫做default -->
<slot></slot>
</div>
</template>
<MyCount :init="myCountNum" @numchange="getCount">
<!-- 默认情况下,在使用组件的时候,提供的内容都会被填充到名字为default的插槽之中 -->
<p>这是插槽</p>
</MyCount>
注意
每一个插槽slot必须有一个name名称,默认name的值为default
# 14.11.1 具名插槽
<template>
<div>
<p>这是Count组件</p>
<p ref="count1">当前值为: {{ count }}</p>
<button @click="add">+1</button>
<!-- 给插槽定义一个name名称 -->
<slot name="footer">默认内容</slot>
</div>
</template>
<MyCount :init="myCountNum" @numchange="getCount">
<!-- 1.如果要把内容填充到指定名称的插槽中,需要使用v-slot这个指令 -->
<!-- 2. v-slot: 后面要跟上插槽的名字 -->
<!-- 3. v-slot: 指令不能直接用在元素身上,必须用在template标签上 -->
<!-- 4. template这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的html元素 -->
<template v-slot:footer><!-- <template #footer> -->
<p>这是插槽</p>
</template>
</MyCount>
TIP
v-slot的语法糖形式是#,即#footer
# 14.11.2 作用域插槽
<slot name="footer" >默认内容</slot>
<MyCount :init="myCountNum" @numchange="getCount">
<!-- 接受传过来的值msg并重命名为scope -->
<!-- <template #footer="{ msg }"> -->
<template #footer="scope">
<p>这是插槽</p>
<!-- 使用值 -->
<!-- <p>{{ msg }}</p> -->
<p>{{ scope.msg }}</p>
</template>
</MyCount>
<template>
<div>
<p>这是Count组件</p>
<p ref="count1">当前值为: {{ count }}</p>
<button @click="add">+1</button>
<!-- msg是预留的,可能用得到的值 -->
<slot name="footer" msg="这是尾部"></slot>
</div>
</template>
← 前端笔记