vuecli 的 tips
- 1.开启终端
ctrl+esc下面的键 或者点击终端-新建终端
- 2.关闭eslint
vue.config.js 里配置 lintOnSave:false 重启项目
- 3.解析顺序
import 再 配置项 再 渲染
- 4.npm相关
可以查询有多少版本 npm view webpack version less-loader npm view less-loader version (567里选建议7)
1.ref 获取dom元素/获取组件的实例对象vc
- 1.写法
<!-- this.$refs.title 获取dom元素 -->
<h1 v-text="msg2" ref="title"></h1>
<!-- this.$refs.school 获取组件的实例对象 -->
<School ref="school" />
console.log(this.$refs.title); // 真实的dom元素
console.log(this.$refs.school); // 拿到组件的实例对象
- 2.代码示例
/*App.vue*/
<!-- this.$refs.title 获取dom元素 -->
<h1 v-text="msg2" ref="title"></h1>
<!-- this.$refs.school 获取组件的实例对象 -->
<School ref="school" />
<button @click="showDom">点击输出上方两个dom元素</button>
<script>
// 引入组件
import School from './components/School'
import Student from './components/Student'
export default {
name: 'App',
components: {
School,
Student,
},
data() {
return {
msg: '拿下月薪过w的offer',
msg2:'成功了'
}
},
methods: {
showDom() {
console.log(this.$refs.title); // 真实的dom元素
console.log(this.$refs.school); // 拿到组件的实例对象
console.log(this.$refs.student); // 拿到组件的实例对象
}
}
}
</script>
/*School.vue 组件*/
<template>
<div class="demo">
<h2>学校名称:{{ name }}</h2>
<h2>学校地址:{{ address }}</h2>
<button @click="showName">点我提示学校名</button>
</div>
</template>
<script>
// 直接暴露组件的配置对象
export default {
name: 'School',
data() {
return {
name: '尚硅谷',
address: '北京昌平'
}
},
methods: {
showName() {
alert(this.name)
}
},
}
</script>
<style>
.demo {
background-color: orange;
}
</style>
3.mixin混入 复用配置
1. 功能:可以把多个组件共用的配置提取成一个混入对象
2. 使用方式:
第一步定义混入:
每一个配置项都可以写进mixin.js里面 并暴露出去
```
{
data(){....},
methods:{....}
....
}
```
第二步使用混入:
全局混入 main.js里写:```Vue.mixin(xxx)``` 所有vm vc都会得到这个代码片段
局部混入:```mixins:['xxx'] ```
- 代码示例
4.插件的使用
1.创建plugins.js文件(带方法install和参数)
1.全局过滤器 Vue.filter('mySlice',function(val){})
2.全局指令 Vue.directive('fbind',{})
3.定义混入 Vue.mixin({})
4.给Vue原型上添加一个方法 Vue.prototype.hello = ()=>{alert('你好啊')} (vm和vc就都能用了)
2.引入并使用 Vue.use(plugins)
1. import plugins from 'plugins' 引入main.js
2. Vue.use(plugins) 应用插件
- 代码案例
- main.js 入口文件引入插件并注册
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 引入插件
import {plugins} from './plugins'
// 应用插件
Vue.use(plugins,1,2)
new Vue({
render: h => h(App),
}).$mount('#app')
- plugins.js 插件文件
// 定义一个插件
export const plugins = {
// vue的构造函数 可以写过滤器 自定义指令 mixin
install(Vue,a,b) {
console.log('install',a,b, 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.prototype.hello = () => {
alert('hello')
}
// 混入
Vue.mixin({
data(){
return {
plugins_msg:'plugins里面的mixin'
}
}
})
}
}
- App.vue 使用
<template>
<div>
<!-- 使用插件plugins -->
<div>
全局混入 plugins_msg 以及 全局过滤器 显示[0,4)<br>
{{ plugins_msg | mySlice}} <br>
全局指令 默认获取焦点 <br>
<input type="text" v-fbind:value="plugins_msg"><br>
<button @click="hello">点我触发hello全局方法</button>
</div>
</div>
</template>
5.scoped样式 防止样式冲突
<style scoped> scoped 表示是局部样式 防止冲突 </style>
App.vue里无效 所有的组件都汇总到App里头
<style lang="css">less 或者 sass 或者 css</style>
比如用less 那么会会提示你安装less -loader 那就 npm i less-loader
6.组件-自定义事件(组件间通信 2种绑定写法)
- 使用场景
1.A是父组件 B是子组件 B给A传数据 那么就要在A中给B绑定自定义事件(事件的回调在A中)
2.通过this.$ref.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods,要么用箭头函数,
否则this指向会出问题(function的会指向就是vc了就不对了,因为事件回调应该在A中)同下ref的注意事项
- 绑定1:@atguigu="getValue" <=> this.$emit('atguigu',this.name);
1.父组件给调用的子组件绑定自定义事件@xxx="getValue"
2.getValue(value){console.log(value)}; // 收到子组件传递过来的value
3.子组件定义一个方法,按钮点击触发this.@emit('自定义事件名xxx',value)传递value
- 代码示例
/* App.vue */
/* 渲染了马上atguigu自定义事件就会挂在Student身上 */
<Student v-on:atguigu="getStudentName"/>
<Student @atguigu="getStudentName"/>
/* 点击Student组件的click的时候触发atguigu就触发getStudentName */
getStudentName(name,...params){
// 第一个参数,剩下的全都是params数组里
console.log(name)
}
/* Student.vue 组件 */
// vc触发Student身上自定义的atguigu事件就能调用App.vue身上的getStudentName函数
<button @click="sendStudentName">把学生名字给App</button>
sendStudentName(){
this.$emit('atguigu',this.name,a,b,c); // 除了this.name 其它参数传过去数组接收
}
- 绑定2:$ref
/* 需求:可以等几秒再绑定自定义事件 写在mounted钩子里 */
this.$ref.student.$on('atguigu',this.getStudentName); // 绑定自定义事件atguigu 触发getStudentName
this.$ref.student.$once('atguigu',this.getStudentName); // 只能触发一次
- $ref注意事项
不能把父组件调用的事件写在回调里面 除非写成箭头函数 会往上层找this 就是mounted的this那就可以调用了
- 解绑:$off 三种写法
/* Student.vue */
this.$off('atguigu'); // 解绑atguigu
this.$off(['atguigu','atguigu2','atguigu3']); // 解绑atguigu 以及atguigu2 atguigu3
this.$off(); // 全解绑
7.组件-注释事项
- 绑定原生事件 .native 否则会当作自定义事件
<Student @click.native="show"/>
8.全局事件总线(任意组件间通信方法1 $sub) - 文件:vue_test3_custom_sub_pubsub
1. 一种组件间通信的方式,适用于<span style="color:red">任意组件间通信</span>。
2. 安装全局事件总线:
js
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
......
})
3. 使用事件总线:
1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的
<span style="color:red">回调留在A组件自身。</span>
```js
methods(){
demo(data){......}
}
......
mounted() {
this.$bus.$on('xxxx',this.demo)
}
```
2. 提供数据:```this.$bus.$emit('xxxx',数据)```
4. 最好在beforeDestroy钩子中,用$off去解绑<span style="color:red">当前组件所用到的</span>事件。
9.消息发布与订阅(任意组件间通信方法2 pubsub) - 文件:vue_test3_custom_sub_pubsub
pubsub.js 任何框架里消息订阅发布 npm i pubsub.js
- School.vue 接收数据
/* School.vue */
import pubsub from 'pubsub-js' // 订阅的地方要引入
mounted(){
this.pubId = pubsub.subscribe('hello',(msgName,data) => {
console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data);
console.log(this); // vc
})
},
beforeDestroy(){
pubsub.unsubscribe(this.pubId); // 取消订阅
}
- Student.vue 发布数据
/* Student.vue */
import pubsub from 'pubsub-js' // 发布的地方也要引入
methods:{
sendStudentName(){
pubsub.publish('helllo',666); // 提供数据
}
}
10.todoList 案例1-功能+存储 - 文件:vue_test2_todolist (props传值传方法)
- 组件:实现界面局部功能代码和资源的集合
- 文件:vue_test2_todolist
1. 组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(<span style="color:red">状态提升</span>)。
(3).实现交互:从绑定事件开始。
2. props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
11.todolist 案例2-功能完整版 5种通信方式(重要且完整)
- 文件:vue_test4_todolist_plus_edit 代码看readme.md
- 组件通信的5种方法
1. 父子组件通信 props 接收参数和方法
2. 父子组件通信 绑定自定义事件 $ref $on 的方法
3. 父子组件通信 绑定自定义事件 @xxx this.$emit
4. 任意组件通信 全局事件总线 $sub
5. 任意组件通信 消息订阅与发布 npm i pubsub-js 插件$pubsub
- 注意事项
props是不能修改的
let obj = {a:1,b:2}
obj.a = 666; // 改某个属性值 不能监测到 所以如果只是改值了 发现不了不会报错
obj = {x:100,y:200}; // 整个对象都改了 能检测到
- 思路分析
- header 组件
App 组件 (对todoList增删改查操作数据 watch监听 localStorage存储)
------------------------------------------------------------------
【子传父方法1:props】(input框 取值 回车 触发事件传值给App unshift)
input框输入:
逻辑:获取到input的value传给App新增进todoList数组并存储
1.获取input里的值 e.target.value 或者 v-model="title"绑定一个data
2.构造一个新对象传给todoList 调用props过来的App.vue里的方法
3.id用 nsnoid 精简版uuid npm i nanoid import {nanoid} from 'nanoid'
4.清空input的值 this.title=''
5.一进页面focus input
6.校验input的数据格式 if(!this.title.trim()) return alert('输入不能为空')
item:
1.<input type="text" v-model="title" @keyup.enter="sendInputValue" />
2.sendInputValue(e) {
if(!this.title.trim()) return alert('输入不能为空')
const todoObj = { id: nanoid(), title: this.title, done: false, edit: false };
this.addTodoItem(todoObj)
this.title = ''
}
App:
新增:props父接收子(子传父方法1)
逻辑:获取header传来的值 新增todoList 并本地存储
1. addTodoItem(todo) 父组件定义一个方法传给子组件header接收todo
2. methods addTodoItem 接收到todo unshift进todoList并深度监听存储
3. watch深度监听 todoList 并 localStorage 存储todoList
watch:{
todoList :{
deep:true,
handler(value){
localStorage.setItem('person',JSON.stringify(obj));
}
}
- list 组件
1.列表渲染 props (包含item组件 渲染列表)
a) 引入item组件,v-for循环item
b) 数据从App里引入 props引入数据
- item 组件
【子传父方法2:全局事件总线】(勾选 删除 编辑 confirm forEach )
1.勾选:全局事件总线(子传父方法2)
逻辑:触发change事件取id 回传给App 取反对应的id
1.@change 获取到id回传 this.$bus.$emit('',id)
2.checkTodoItem(id){forEach}循环取反对应的
3.mounted():父绑定 this.$bus.$on('checkTodoItem', this.checkTodoItem)
4.beforeDestroy():this.$bus.$off('checkTodoItem')
2.删除:全局事件总线(子传父方法2)
逻辑:button绑定handelDelete获取到item的id传给App在那过滤组成新数组
1.mounted():父绑定 this.$bus.$on('delTodoItem', this.delTodoItem)
2.beforeDestroy():this.$bus.$off('delTodoItem')
3.this.$bus.$emit('delTodoItem',id) 子传父 加个confirm确认是否删除
4.this.delTodoItem 父执行回调处理逻辑过滤出新数组保存data
item:
methods: {
handelDelete(id) {
if(confirm('确定删除吗?')){
this.$bus.$emit('delTodoItem',id)
}
}
},
App:
mounted() {
// 绑定自定义全局事件总线接收item的id删除todo
this.$bus.$on('delTodoItem', this.delTodoItem)
},
beforeDestroy() {
// 解绑自定义全局事件总线
this.$bus.$off('delTodoItem')
}
3.编辑(原数据新增一个属性并且修改值响应式用Vue.$set + setTimeout/$nectTick + 子传父方法2)
逻辑:点击变成input框,编辑完成显示渲染
编辑:handlerEdit 修改input显示并获取焦点&隐藏
失去焦点:handlerBlur 真正执行编辑的回调函数跟App通信
1.绑定handelEdit在button上
- 1.定义一个input v-show变输入框并获取焦点
a. 定义ref="inputTitle"
b. 拿到元素调用focus()获取焦点 tihs.$refs.inputTitle.focus()
c. 由于vue会等整段代码都执行完了才重新解析模板,所以此时获取不到input
1) setTimeout(()=>{包一个定时器focus()},200)
2) this.$nextTick(function(){this.$refs.input.focus()})
指定的回调会在dom更新完毕之后执行
- 2.判断身上有无edit属性,若无则新增,有则仅修改edit状态为true
a. if(todo.hasOwnProperty('isEdit')){todo.idEdit=true}
b. 否则 this.$set(todoList,'edit',true)
2.绑定handlerBlur事件在input上
逻辑:localStorage让他保持编辑状态了一进来就会显示input所以要修改这个bug
- 1.将按钮改为确认
- 2.绑定@blur事件 handleBulr(todo) input
a. 失去焦点的时候变为正常渲染
b. todo.isEdit=false // 修改状态 这里不就直接改props了吗??
- 3.传递id与修改后的title给App this.$bus.$emit('editTodoList',id)
3.updateTodo(id,title){} App里真实执行回调绑定与解绑
- 1.找到对应的id 修改title
5.细节问题:handlerEdit handleBulr
- 1.handlerEdit点击编辑按钮的时候 获取焦点
- 2.handleBulr失去焦点的时候 要判空 再传数据 return alert 输入不能为空
- footer 组件
(全选 勾选的数据 清除勾选 cpmputed reduce 子传父第3/4种方法:组件自定义事件)
1.数量判断:
1.已完成 computed + reduce 计算
2.全部 computed 计算todoList长度
<span>已完成{{ doneTotal }}</span> / 全部{{ allTotal }}
computed: {
allTotal(){
return this.todoList.length
},
doneTotal() {
return this.todoList.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)
}
}
===============================================================================
【子传父方法3:组件自定义事件】 @自定义事件="回调函数名字" this.$emit('回调函数',实参)
2.全选
1. <input type="checkbox" v-model="isAll"/>
2. computed 计算 isAll : { get() { return 已完成数跟总数是否相同且总数非0 0的时候就没必要勾上全选了}} }
3. computed 计算 isAll : { set() { return 当isAll被修改的时候 input类型是check 所以isAll的值就是chenked为true或者false 那么被修改的时候就调用 this.$emit('checkAllTodo',value) 将value传过去}}
4. App那使用组件自定义事件 Methods回调"checkAllTodo"接收value 是true还是false 选择用forEach遍历 将done改为对应的全true或者全false
5. 自定义事件在标签里:<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
6. 一样可以解绑this.$off('clearAllTodo'); // 在哪儿解绑?
代码片段:
App:
<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
Footer:
isAll:{
//全选框是否勾选
get(){
return this.doneTotal === this.total && this.total > 0
},
//isAll被修改时set被调用
set(value){
// this.checkAllTodo(value)
this.$emit('checkAllTodo',value)
}
}
===============================================================================
【子传父方法4:组件自定义事件】 this.$ref.Footer.$on() this.$emit() this.$off()
3.清除已完成任务
a) 自定义事件在标签里:<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
b) 同上全选/反全选 clearAllTodo 将所有todo为done的移除 用filter剩下done为false的
c) 换一种写法 用 $ref this.$refs.Footer.$on('clearAllTodo',this.clearAllTodo); // 获取到Footer的dom元素
d) 优点:可以等几秒再绑定自定义事件 写在mounted里面
e) 触发:同上 this.$emit('clearAllTodo',id,b,c,d)
f) App里接收的回调函数 clearAllTodo(id,...params) 这样bcd会以[b,c,d]的形式返回接收到
===============================================================================
【子传父方法5:消息订阅与发布】
npm i pubsub.js
import pubsub from 'pubsub-js';
monuten():this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo)
beforeDestroy:pubsub.unsubscribe(this.pubId)
12.$nextTick(function(){})
作用:在下一次dom更新结束后执行指定的回调
啥时候用:当数据改变后,要dom更新之后再进行某些操作,就在nextTick指定回调种执行,参考11 编辑案例 input 点击编辑 修改edit为true之后 要focus()
也可以定时器,定时器就算时间到了