# cq5-vue-basic
**Repository Path**: itcasttestone/cq5-vue-basic
## Basic Information
- **Project Name**: cq5-vue-basic
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2021-11-10
- **Last Updated**: 2022-07-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Vue基础
Vue主要特点:组件化(html/css/js) 虚拟DOM 双向数据绑定
## 脚手架
Vue Cli - Vue Command Line 提供了一些列命令,让我们快速创建项目结构
### 安装
```bash
npm install -g @vue/cli
# OR
yarn global add @vue/cli
```
### 初始化项目
```bash
vue create 项目名称
```



## 单文件组件
```vue
App组件
```
> 注意:template中的根节点,只能有一个。
## 插值表达式⭐
完成hello world
插值表达式,就是展示data中定义的数据
```vue
{{ msg }}
{{ "🤳" + msg }}
{{ age + 1}}
{{ age >= 18 ? '已成年' : '未成年' }}
{{ msg.split('').reverse() }}
```
## MVVM
M - Model 数据模型
V - View 视图模型
VM - ViewModel 一个对象,连接数据模型和视图模型,它能够监听数据的变化,将变化映射到视图
## 指令
在Vue中,以`v-`开头的,都叫做指令。
### v-text
```vue
```
v-text的合法使用和插值表达式类似,主要区别在于:1. v-text会修改标签textcontent的所有内容,而{{}}可以更改局部;2. 书写位置不一样, v-text书写在属性位置,而{{}}书写在标签内容部分,**{{}}不能书写在属性位置**
### v-html
```vue
```
### v-bind⭐
v-bind用于动态绑定属性
```vue
百度
百度
javascript是世界上最好的语言
javascript是世界上最好的语言
hello
world
```
### v-on⭐
v-on主要 用于绑定事件 语法 `v-on:事件类型="回调函数"` `简写: @事件类型="处理函数" `
处理函数中可以直接传递参数( 传递事件对象通过$event,注意不能加引号)
```vue
```
#### 事件修饰符
.stop 阻止冒泡
.prevent 阻止默认行为
#### 按键修饰符 和键盘事件相关
.enter 表示按回车键
### 作业
1. 写一个开关灯案例
2. 写一个折叠展开效果,注意折叠的按钮需要带旋转效果
### v-show ⭐
语法:`v-show="布尔值"`
v-show通过控制样式来控制元素的显示和隐藏
### v-if⭐
语法:`v-if="布尔值"`
通过操作dom来控制元素的显示和隐藏
>注意:v-show和v-if使用场景的区别
>
>v-show: 频繁显示隐藏元素时适用
>
>v-if: 异步情况或者需要重新渲染组件时使用
### v-for⭐
语法:`v-for=(item, index) in 数组 :key="唯一的值"` key用来唯一标识数组的每一项
数据更新,视图也会自动更新,但是以下内容除外
1. 通过length属性修改数组
2. 通过索引修改数组
3. 利用不会修改原数组的方法:filter concat slice
解决办法:
1. 使用响应式方法: push() pop() shift() unshift() splice() sort() reverse()
2. 使用Vue.set() 或者 this.$set() 方法
> 注意:v-for和v-if不要一起使用
### v-pre
### v-cloak
### v-once
## 过滤器⭐
局部过滤器
```js
filters: {
// 参数表示需要过滤的数据
过滤器名字(v) {
return xx值
}
}
```
过滤器使用
`{{ 数据 | 过滤器名字 }}`
``
## 计算属性⭐
定义:根据data中已有的属性计算得到一个新的属性
优点:依赖data中的数据进行缓存,性能比较高
缺点:不能够在里面处理异步数据
```js
computed: {
计算属性名() {
return xx值
}
}
```
```js
computed: {
计算属性名: {
get: function() {},
set: function(v) {}
}
}
```
## 监听器⭐
定义:可以监听data中数据的变化
优点:可以在里面使用异步操作
缺点:性能较低
```
watch: {
data中属性名(newVal, oldVal) {}
}
```
```js
watch: {
data中属性名: {
// 监听属性改变时的触发函数,不能修改handler名字,只能叫这个名字
handler(){},
// 深度监听
deep: true,
// 立即执行
immediate: true
}
}
```
## 组件⭐
组件: 把它当成一块一块的积木,积木中包含了 html/css/js
模块: 把它当成一块一块的积木,积木中包含了js
使用组件好处:便于维护,提升开发效率
### 准备工作






### 创建组件
1. 在 src/components 下面新建一个后缀是 `.vue`格式的文件 比如 `my-button.vue`
2. 完善组件的template/script/style 指定style语言为less``
3. 将创建的my-button组件,在App.vue文件中引入并使用
```
// 1. 引入button组件
import mybutton from './components/my-button.vue'
export default {
name: 'App',
// 2. 注册组件
components: {
mybutton
}
}
```
4. 自动处理eslint报错
1. 安装vs code的`eslint`插件
2. 必须将你的项目根目录拖拽到vscode中,项目目录前面不能再有其他文件加了
3. 添加vscode配置
```json
// 保存后自动修复格式
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// 添加vue支持
"eslint.validate": [
"javascript",
"javascriptreact",
"vue"
],
```
### 组件通信
1. 父 --> 子
1. 子组件中需要定义个变量来接受父组件传递过来的值。如何定义:通过props属性定义,它后面可以跟一个数组。比如`props: ['txt']`
2. 父组件给子组件传递数据 `` 其中msg是父组件中的数据
> 注意:
>
> 1. 上面的属性写法不能设置默认值,也不能对用户传递的数据类型做限制
> 2. props只能读取,不能修改 (单向数据流)
2. 子 --> 父
1. 子组件通过调用$emit()函数,将数据发射给父组件。发射时需要携带以下参数:1. 自定义事件名称;2,发射的数据
2. 父组件中,通过v-on指令监听子组件发射过来的事件,并在事件的处理函数中获取发射过来的数据,通过函数参数获取值
3. 跨层级
事件总线: 它是一个空的Vue实例,事件的监听和触发都通过这个空的事件总线来执行
```js
import Vue from 'vue'
export default new Vue()
```
4. ref
通过ref去获取组件实例,可以调用组件实例中定义好的方法,并给这个方法传值。
## 作业
1. 封装下面组件 `tag`

这个组件接受两个属性,一个是icon,另外一个是txt。
属性的值从父组件中传递过来,父组件的数据长下面这样
```js
arr: [
{ id: 1, icon: "🚣♀️", title: "水" },
{ id: 2, icon: "🌗", title: "星空" },
{ id: 3, icon: "🍏", title: "水果" },
{ id: 4, icon: "🚧", title: "山" },
],
```
2. 封装组件 `card`

这个组件接受的属性,是个对象
属性的值从父组件中传递过来,父组件的数据长下面这样
```js
arr: [
{id: 1, author: 'jack', avatar: 'https://images.pexels.com/users/avatars/126286496/yaroslava-borz-130.jpeg?auto=compress&fit=crop&h=60&w=60', img: 'https://img1.baidu.com/it/u=1261014649,765298546&fm=26&fmt=auto'},
{id: 2, author: 'jack', avatar: 'https://images.pexels.com/users/avatars/126286496/yaroslava-borz-130.jpeg?auto=compress&fit=crop&h=60&w=60', img: 'https://img1.baidu.com/it/u=1261014649,765298546&fm=26&fmt=auto'},
{id: 3, author: 'jack', avatar: 'https://images.pexels.com/users/avatars/126286496/yaroslava-borz-130.jpeg?auto=compress&fit=crop&h=60&w=60', img: 'https://img1.baidu.com/it/u=1261014649,765298546&fm=26&fmt=auto'}
]
```
新增需求:点击左下角的图标,可以实现删除一条数据
3. 封装两个组件,一个wrapper组件,一个goods组件,如图所示:

其中wrapper组件接受属性:icon表示图标, title表示标题, type表示分类
goods组件接受属性:img表示图片,title表示商品名称,desc表示销售信息。
注意,所有的数据放在**爷爷组件**中,**爷爷组件**的数据类似下面这样:
```js
obj: {
icon: '⚛',
title: '淘宝*天猫',
type: '数码家电热销榜',
goodsList: [
{id: 1, img: 'https://img.pddpic.com/mms-material-img/2021-11-04/0cb60fbd-6d36-4ee8-8a78-17a7361a7e9c.jpeg.a.jpeghttps://img.pddpic.com/mms-material-img/2021-11-04/0cb60fbd-6d36-4ee8-8a78-17a7361a7e9c.jpeg.a.jpeg', title: '湘大王鲜卤鸭脖批发鸭脖子风干手撕口味鸭 原价¥3.99 券后¥2.99', desc: '月销375件'}
]
}
```
新增需求:点击goods组件,可以实现删除一条数据
## 生命周期⭐
生命周期指的是就是一个组件的生老病死的过程。
```js
// 实例创建完成后自动调用,这里已经完成了数据的检测,但是还没有挂载,$el还无法使用
// 开发时,常在它里面去发送异步请求获取数据 ⭐ (另外一个原因是,如果用vue做服务端渲染的话,它没有mounted这个生命周期)
created() {
console.log("实例创建之后");
console.log(this.msg);
console.log(this.$el);
},
// 组件挂载以后调用,如果项目中有操作dom,请在这里完成
mounted() {
console.log("挂载之后");
console.log(this.msg);
console.log(this.$el);
},
// 实例销毁之前调用,一般用来清除定时器,或者解绑事件
beforeDestroy() {
console.log("销毁之前");
console.log(this.msg);
console.log(this.$el);
},
```
如果存在父子组件,那么生命周期的执行顺序是怎么样的?
`父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted`
换句话说,儿子mounted成功了,父亲才会mounted。
`父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated`
换句话说,儿子updated成功了,父亲才会updated。
## ref ⭐
ref 可以用来获取组件实例的引用,或者获取dom引用
## this.$nextTick(回调函数)⭐
$nextTick会等页面dom更新完成之后,再执行回调函数中的代码
## 动态组件
利用Vue的内置组件component 的is属性来指定需要渲染哪个组件。is属性的值是组件的名字
## 缓存组件 ⭐
keep-alive
## 插槽⭐
使用插槽的好处,让组件的模板复用度更高! 一般在封装公共组件才会用到,普通的业务代码基本上用不到,用也是用别人封装好的。
1. 默认插槽
```vue
// mybutton组件中innerHTML位置的任何内容都会插入到mybutton组件内部模板的slot组件中
🎨
主要按钮
```
2. 具名插槽
```vue
🎨
主要按钮
```
3. 作用域插槽
```vue
{{ scope.msg }}
```
## 自定义指令
作用就是拓展内置指令
> 注意:自定义指令的钩子函数中,是不能够获取组件实例的this的
```js
directives: {
clickoutside: {
// 1. 现在页面只有button有点击事件,其他地方没有点击事件,所以还需要绑定一个点击事件,click绑定给谁
// 2. 自定义指令中需要有判断逻辑,只要判断点击了main盒子外面,就要执行handleClose函数。
bind: function (el, binding) {
// console.log(el, binding);
function handler(e) {
// el是绑定的灰色盒子, e.target表示的是触发click事件的元素
// 如果el包含了e.target 就说明点击的是里面,那么此时什么都不需要做,直接return就行了
// console.log(el, e.target);
if(el.contains(e.target)) return false
// 如果点击外面,就直接执行函数 handleClose
binding.value()
}
// 把handler函数绑定到el身上,这样的话下面的unbind函数中,在移除事件时,也能够通过el获取到事件函数
el._handler = handler
document.addEventListener("click", el._handler);
},
unbind: function (el) {
console.log('解绑了');
document.removeEventListener("click", el._handler);
},
},
},
```
## 路由
`index.js`
```js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../views/index.vue'
// 表示启用vue-router插件
Vue.use(VueRouter)
// 表示一个路由配置规则数组,里面包含各种路由规则配置对象
const routes = [
// 第一条路由规则配置对象
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/mine',
name: 'Mine',
// 开启路由组件懒加载,当你访问这条路由规则时,才会加载该文件 里面的注释表示打包后的js文件名
component: () => import(/* webpackChunkName: "mine" */ '../views/mine.vue')
}
]
// 里面上面的路由规则,创建一个路由对象
const router = new VueRouter({
routes
})
export default router
```
`App.vue`
```vue
首页
我的音乐
```
## 跳转传参
1. 首先路由规则中定义参数
```js
{
// 通过在路径后面添加 /:参数名 来定义路由参数
path: '/detail/:song',
name: 'Detail',
component: () => import(/* webpackChunkName: "detail" */ '../views/detail.vue')
}
```
2. 点击跳转传递参数
```vue
{{item.song}}
```
3. 页面中获取参数
```
模板中 {{$route.params.参数名}}
js中 this.$route.params.参数名
```
## 编程式导航
```js
// 直接传路径字符串
// this.$router.push(`/detail/${song}`)
// 传对象
// this.$router.push({ path: `/detail/${song}` })
this.$router.push({ name: 'Detail', params: { song } })
```
## 路由重定向
```js
{
// 不满足上面规则的所有规则
path: '*',
// 重定向
// redirect: '/404'
redirect: { name: 'NotFound' }
}
```
## 路由嵌套
1. 需要在路由规则中定义一个嵌套路由规则
2. router-view组件也需要放在一个有children嵌套规则的组件下面
## 导航守卫
```js
router.beforeEach((to, from, next) => {
console.log('路由守卫执行了')
const token = localStorage.getItem('token') || ''
// 如果访问的不是Login 并且 还没有登录, 这种情况你能跳转到login
if (to.name !== 'Login' && !token) next({ name: 'Login' })
// 否则你就可以正常访问
else next()
})
```
## 监听路由参数变化