Vue CLI ref props mixin plugin scoped

ref

ref被用来给元素或子组件注册引用信息(id的替代者)

  1. 应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象vc
  2. 使用方式
    1. 打标识:

    2. 获取:this.$refs.xxx
<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
    <School ref="sch"/>
  </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组件的实例对象(vc)
      }
    },
  }
</script>

props

props让组件接收外部传过来的数据

  1. 传递数据这里age前加:,通过v-bind使得里面的18是数字

  2. 接收数据

    第一种方式(只接收)props:['name', 'age'] 第二种方式(限制类型)props:{name:String, age:Number} 第三种方式(限制类型、限制必要性、指定默认值)

props: {
    name: {
        type: String,	 // 类型
        required: true,// 必要性
        default: 'cess'// 默认值
    }
}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中,然后去修改data中的数据

App.vue

<template>
  <div>
    <Student name="李四" sex="女" :age="18"/>
    <Student name="王五" sex="男" :age="18"/>
  </div>
</template>

<script>
  import Student from './components/Student'

  export default {
    name:'App',
    components:{ Student }
  }
</script>

Student.vue

<template>
  <div>
    <h1>{{ msg }}</h1>
    <h2>学生姓名:{{ name }}</h2>
    <h2>学生性别:{{ sex }}</h2>
    <h2>学生年龄:{{ myAge + 1 }}</h2>
    <button @click="updateAge">尝试修改收到的年龄</button>
  </div>
</template>

<script>
export default {
  name: "Student",
  data() {
    console.log(this);
    return {
      msg: "我是一个UESTC大学的学生",
      myAge: this.age,
    };
  },
  methods: { updateAge() { this.myAge++; }, },
  // 简单声明接收
  // props:['name','age','sex']

  // 接收的同时对数据进行类型限制
  //   props: {
  //     name: String,
  //     age: Number,
  //     sex: String,
  //   }

  // 接收的同时对数据:进行类型限制+默认值的指定+必要性的限制
  props: {
    name: {
      type: String, 	//name的类型是字符串
      required: true, //name是必要的
    },
    age: {
      type: Number,
      default: 99, //默认值
    },
    sex: {
      type: String,
      required: true,
    },
  },
};
</script>

mixin

  1. 功能:可以把多个组件共用的配置提取成一个混入对象

  2. 使用方式

    a.定义混入

    const mixin = {
        data() {....},
        methods: {....}
        ....
    }
    

    b.使用混入

    ​ ⅰ全局混入Vue.mixin(xxx) ​ ⅱ局部混入mixins:['xxx']

  3. 备注

    • 组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先

      var mixin = {
      	data: function () {
      		return {
          		message: 'hello',
                  foo: 'abc'
          	}
        	}
      }
      
      new Vue({
        	mixins: [mixin],
        	data () {
          	return {
            		message: 'goodbye',
                  	bar: 'def'
          	}
          },
        	created () {
          	console.log(this.$data)
          	// => { message: "goodbye", foo: "abc", bar: "def" }
        	}
      })
      
    • 同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用

      var mixin = {
        	created () {
          	console.log('混入对象的钩子被调用')
        	}
      }
      
      new Vue({
        	mixins: [mixin],
        	created () {
          	console.log('组件钩子被调用')
        	}
      })
      
      // => "混入对象的钩子被调用"
      // => "组件钩子被调用"
      

mixin.js

export const hunhe = {
	methods: {
		showName(){
			alert(this.name)
		}
	},
	mounted() {
		console.log('你好啊!')
	},
}

export const hunhe2 = {
	data() {
		return {
			x:100,
			y:200
		}
	},
}

School.vue

<template>
  <div>
    <h2 @click="showName">学校名称:{{name}}</h2>
    <h2>学校地址:{{address}}</h2>
  </div>
</template>

<script>
  //引入一个hunhe
  import {hunhe,hunhe2} from '../mixin'

  export default {
    name:'School',
    data() {
      return {
        name:'尚硅谷',
        address:'北京',
        x:666
      }
    },
    mixins:[hunhe,hunhe2]	// 局部混入
  }
</script>

Student.vue

<template>
  <div>
    <h2 @click="showName">学生姓名:{{name}}</h2>
    <h2>学生性别:{{sex}}</h2>
  </div>
</template>

<script>
  import {hunhe,hunhe2} from '../mixin'

  export default {
    name:'Student',
    data() {
      return {
        name:'张三',
        sex:'男'
      }
    },
    mixins:[hunhe,hunhe2]	// 局部混入
  }
</script>

App.vue

<template>
  <div>
    <School/>
    <hr>
    <Student/>
  </div>
</template>

<script>
  import School from './components/School'
  import Student from './components/Student'

  export default {
    name:'App',
    components:{School,Student}
  }
</script>

main.js

import Vue from 'vue'
import App from './App.vue'
// import {mixin} from './mixin'

Vue.config.productionTip = false
// Vue.mixin(hunhe)		// 全局混合引入
// Vue.mixin(hunhe2)	// 全局混合

new Vue({
    el:"#app",
    render: h => h(App)
})

plugin

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
  3. 定义插件(见下 src/plugin.js)
  4. 使用插件:Vue.use()

plugin.js

export default {
  install(Vue,x,y,z){
    console.log(x,y,z)
    //全局过滤器
    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.mixin({
      data() {return {x:100,y:200}},
    })

    //给Vue原型上添加一个方法(vm和vc就都能用了)
    Vue.prototype.hello = ()=>{alert('你好啊')}
  }
}

main.js

import Vue from 'vue'
import App from './App.vue'
import plugins from './plugins'	// 引入插件

Vue.config.productionTip = false

Vue.use(plugins,1,2,3)	// 应用(使用)插件

new Vue({
	el:'#app',
	render: h => h(App)
})

School.vue

<template>
  <div>
    <h2>学校名称:{{ name | mySlice }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <button @click="test">点我测试一个hello方法</button>
  </div>
</template>

<script>
  export default {
    name:'School',
    data() {
      return {
        name:'尚硅谷atguigu',
        address:'北京',
      }
    },
    methods: {
      test(){
        this.hello()
      }
    },
  }
</script>

Student.vue

<template>
  <div>
    <h2>学生姓名:{{ name }}</h2>
    <h2>学生性别:{{ sex }}</h2>
    <input type="text" v-fbind:value="name">
  </div>
</template>

<script>
  export default {
    name:'Student',
    data() {
      return {
        name:'张三',
        sex:'男'
      }
    },
  }
</script>

scoped

  1. 作用:让样式在局部生效,防止冲突
  2. 写法:<style scoped>

School.vue

<template>
  <div class="demo">
    <h2 class="title">学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
  </div>
</template>

<script>
  export default {
    name:'School',
    data() {
      return {
        name:'尚硅谷atguigu',
        address:'北京',
      }
    }
  }
</script>

<style scoped>
  .demo{
    background-color: skyblue;
  }
</style>

Student.vue

<template>
  <div class="demo">
    <h2 class="title">学生姓名:{{ name }}</h2>
    <h2 class="atguigu">学生性别:{{ sex }}</h2>
  </div>
</template>

<script>
  export default {
    name: 'Student',
    data() {
      return {
        name: '张三',
        sex: '男'
      }
    }
  }
</script>

<style lang="less" scoped>
  .demo {
    background-color: pink;
    .atguigu {
      font-size: 40px;
    }
  }
</style>

App.vue

<template>
  <div>
    <h1 class="title">你好啊</h1>
    <School/>
    <Student/>
  </div>
</template>

<script>
  import Student from './components/Student'
  import School from './components/School'

  export default {
    name: 'App',
    components: { School, Student }
  }
</script>

<style scoped>
  .title {
    color: red;
  }
</style>

TodoList案例

App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo" />
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
        <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
      </div>
    </div>
  </div>
</template>

<script>
import MyHeader from "@/components/MyHeader.vue";
import MyFooter from "@/components/MyFooter.vue";
import MyList from "@/components/MyList.vue";

export default {
  name: 'App',
  components: {
    MyHeader,
    MyFooter,
    MyList,
  },
  data(){
    return {
      todos: [
        {id: '001', title: '抽烟', done: true},
        {id: '002', title: '喝酒', done: true},
        {id: '003', title: '烫头', done: true},
      ]
    }
  },
  methods:{
    // 添加todo
    addTodo(todo){
      this.todos.unshift(todo)
    },
    //勾选or取消勾选todo
    checkTodo(id){
      this.todos.forEach((todo)=>{
        if (todo.id === id) todo.done = !todo.done
      })
    },
    //删除todo
    deleteTodo(id){
      this.todos = this.todos.filter(todo => todo.id !== id)
    },
    // 全选or取消全选
    checkAllTodo(done){
      this.todos.forEach((todo)=>{
        todo.done = done
      })
    },
    //清除所有已经完成的todo
    clearAllTodo(){
      this.todos.filter((todo)=>{
        return !todo.done
      })
    }
  }
}
</script>

<style>
/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}

.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

MyHeader.vue

<template>
  <div class="todo-header">
    <input type="text" placeholder="请输入你的任务名称,按回车键确认"
           v-model="title" @keyup.enter="add"/>
  </div>
</template>

<script>
import {nanoid} from "nanoid";

export default {
  name: 'MyHeader',
  data() {
    return {
      title:'',
    }
  },
  props:['addTodo'],
  methods: {
    add(){
      // 校验数据
      if(!this.title.trim()) return alert('输入不能为空')
      // 将用户的输入包装成一个todo对象
      const todoObj = { id:nanoid(), title:this.title, done:false }
      // 通知App组件去添加一个todo对象
      this.addTodo(todoObj)
      // 清空输入
      this.title = ''
    },
  },
}
</script>

<style scoped>
/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>

MyList.vue

<template>
  <ul class="todo-main">
    <MyItem v-for="todo in todos" :key="todo.id" :todo="todo" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
  </ul>
</template>

<script>
import MyItem from "@/components/MyItem.vue";

export default {
  name: 'MyList',
  components: {
    MyItem
  },
  props:['todos','checkTodo','deleteTodo'],
  data() {
    return {

    }
  },
  methods: {},
}
</script>

<style scoped>
/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
</style>

MyItem.vue

<template>
  <li>
    <label>
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
      <span>{{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>

<script>
export default {
  name: 'MyItem',
  data() {
    return {}
  },
  methods: {
    // 勾选or取消勾选
    handleCheck(id){
      //通知app组件将对应的todo对象的done值取反
      this.checkTodo(id)
    },
    // 删除
    handleDelete(id){
      if(confirm('确定删除吗?')){
        this.deleteTodo(id)
      }
    }
  },
  props:['todo','checkTodo','deleteTodo'],
}
</script>

<style scoped>
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
}

li label {
  float: left;
  cursor: pointer;
}

li label li input {
  vertical-align: middle;
  margin-right: 6px;
  position: relative;
  top: -1px;
}

li button {
  float: right;
  display: none;
  margin-top: 3px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

li:hover {
  background-color: #ddd;
}

li:hover button {
  display: block;
}
</style>

MyFooter.vue

<template>
  <div class="todo-footer" v-show="total">
    <label>
      <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> -->
      <input type="checkbox" v-model="isAll"/>
    </label>
    <span>
      <span>已完成{{ doneTotal }}</span> / 全部{{ total }}
    </span>
    <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
  </div>
</template>

<script>
export default {
  name: 'MyFooter',
  data() {
    return {}
  },
  props:['todos','checkAllTodo','clearAllTodo'],
  computed:{
    total(){
      return this.todos.length
    },
    doneTotal(){
      return this.todos.reduce((pre,current)=> pre + (current.done ? 1 : 0),0)
    },
    isAll:{
      get(){
        return this.doneTotal === this.total && this.total > 0
      },
      set(value){
        this.checkAllTodo(value)
      }
    },
  },
  methods: {
    // checkAll(e){
    //   this.checkAllTodo(e.target.checked)
    // },
    clearAll(){
      this.clearAllTodo()
    }
  },
}
</script>

<style scoped>
/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
  margin-right: 5px;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}
</style>