# 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 项目名称 ``` ![image-20211110104650776](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211110104650776.png) ![image-20211110104725698](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211110104725698.png) ![image-20211110105527213](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211110105527213.png) ## 单文件组件 ```vue ``` > 注意: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 使用组件好处:便于维护,提升开发效率 ### 准备工作 ![image-20211114092830200](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114092830200.png) ![image-20211114092946975](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114092946975.png) ![image-20211114093008021](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114093008021.png) ![image-20211114093040598](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114093040598.png) ![image-20211114093107606](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114093107606.png) ![image-20211114093135286](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114093135286.png) ### 创建组件 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` ![image-20211114165842355](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114165842355.png) 这个组件接受两个属性,一个是icon,另外一个是txt。 属性的值从父组件中传递过来,父组件的数据长下面这样 ```js arr: [ { id: 1, icon: "🚣‍♀️", title: "水" }, { id: 2, icon: "🌗", title: "星空" }, { id: 3, icon: "🍏", title: "水果" }, { id: 4, icon: "🚧", title: "山" }, ], ``` 2. 封装组件 `card` ![image-20211114171331273](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114171331273.png) 这个组件接受的属性,是个对象 属性的值从父组件中传递过来,父组件的数据长下面这样 ```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'} ] ``` 新增需求:点击左下角的![image-20211114171215290](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114171215290.png)图标,可以实现删除一条数据 3. 封装两个组件,一个wrapper组件,一个goods组件,如图所示: ![image-20211114171814560](C:\Users\Alan\AppData\Roaming\Typora\typora-user-images\image-20211114171814560.png) 其中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 ``` ## 自定义指令 作用就是拓展内置指令 > 注意:自定义指令的钩子函数中,是不能够获取组件实例的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() }) ``` ## 监听路由参数变化