Vue教程3【使用Vue脚手架】render ref props minin scoped $emit $bus 消息订阅发布 动画

npm全局安装

切换淘宝npm镜像
npm config set registry https://registry.npm.taobao.org

全局安装
npm install -g @vue/cli
image-20220118205555410
切换到要创建的目录
vue create xxx
image-20220118205841228 image-20220118210735858
启动项目
npm run serve
image-20220118210930741 image-20220118210955378

vue脚手架结构

image-20220118221548669 image-20220119203348202

加入School和Student组件到components目录下

修改App.vue

修改main.js。注册app组件,加入template

运行npm run serve

image-20220118221856055

render函数

vue.js与vue.runtime.xxx.js的区别

(1)vue.js是完整版的vue,包含:核心功能+模板解析器

(2)vue.runtime.xxx.js是运行版的vue,只包含:核心功能,没有模板解析器

没有模板解析器不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容

main.js

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    /*
    render(createElement){
        return createElement('h2','helloworld');
    }
    */
    render: h => h(App),
}).$mount('#app')

index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
  </body>
</html>

修改默认配置

vue脚手架隐藏了所有webpack配置,使用

vue inspect > output.js

使用vue.config.js对脚手架进行个性化定制,配置参考 | Vue CLI (vuejs.org)

module.exports = {
    pages: {
        index: {
            // page 的入口
            entry: 'src/main.js',
        },
    },
    lintOnSave:false
}

ref属性

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

应用在html标签上获取的是真实Dom元素,应用在组件标签上是组件实例对象(vc)

使用方式
标识 <h1 ref="xxx"></h1><School ref="xxx"></School>
获取:this.$refs.xxx
<Student ref="Stu"></Student>
<button @click="showDom" ref="btn">点击打印元素或组件</button>
methods:{
    showDom(){
      console.log(this.$refs.Stu) //组件实例对象
      console.log(this.$refs.btn) //真实dom元素
    }
  }
image-20220119210406126 image-20220119210332249

props配置

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

(1)传递数据

<School :no="211" name="武汉纺织大学"></School>

(2)接收数据

//(1)只接收
props:['no','name','address']

//(2)限制类型
props:{
    no:Number,
    name:String,
    address:String
}

//(3)限制类型,必要性,默认值
props:{
    no:{
      type:Number,
      required:false,
      default:999
    },
    name:{
      type:String,
      required: true
    },
    address:{
      type:String,
      required:false,
      default: '未知'
    }
}

⚠️props是只读的,vue底层会监测你对props的修改,如果进行修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到新的变量在data中,去进行修改

Shcool.vue

<template>
  <div>
    <h2>编号:{{ CurrentNo }}</h2>
    <h2>学校名称:{{ name }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <button @click="CurrentNo++">点我编号+1</button>
  </div>
</template>

<script>
export default {
  name: "School",
  data() {
    return {
      CurrentNo : this.no
    }
  },
  // props:['no','name','address']
  /*props:{
    no:Number,
    name:String,
    address:String
  },*/
  props:{
    no:{
      type:Number,
      required:false,
      default:999
    },
    name:{
      type:String,
      required: true
    },
    address:{
      type:String,
      required:false,
      default: '未知'
    }
  }
}
</script>

<style scoped>
h2 {
  color: #0dff1d;
}
</style>

app.vue

<template>
  <div>
    <School :no="211" name="武汉纺织大学"></School>
  </div>
</template>
<script>
import School from './components/School'
export default {
  name: "App",
  components: {
     School
  }
}
</script>

minin混入

组件共用的配置提取成一个混入对象

使用方法

(1)定义混合
export const mixin = {
    data(){
        return{
            size: '18cm'
        }
    }
}

(2)使用混合
import {mixin} from '../mixin';
mixins:[mixin] //局部 -组件
Vue.mixin(mixin) //全局 -main.js

插件

功能:用于增强vue

本质:包含install方法的一个对象,第一个参数是Vue,第二个以后的参数是传递的数据

定义插件

export default {
    install(Vue,options) {
        Vue.filters()
        Vue.directive()
        Vue.mixin()
        Vue.prototype.$myMethod=function (){}
    }
}

使用插件

Vue.use(plugin);

scoped样式

让样式在局部生效,防止冲突

<style scoped></style>
image-20220120203846463 image-20220120203904943

扩展

image-20220120204617812
<style lang="less" scoped>
h2 {
  color: #1c036c;
  font-weight: 700;
  h3{
    font-size: 30px;
  }
}
</style>
默认vue不识别其他的语法,需要添加加载器
image-20220120204754969
npm view webpack versions #查看webpack版本
npm view less-loader versions #查看less-loader版本
image-20220120205116528

安装less加载器

npm i less-loader@7 #安装7以上版本
npm install less save-dev

TodoList案例关键点

组件命令注意不要和html元素相同

比如:Header

父组件里面的子组件的子组件import的路径

import TodoItem from "@/components/TodoItem";

(16条消息) Vue解决报错1_This relative module was not found: * ./components/Login.vue in ./src/router/index.js_xiaosi的博客-CSDN博客

父组件个子组件传值

<TodoList :todos="todos"/>
<script>
data() {
    return {
      todos: [
        {id: '001', title: '吃饭', done: false},
        {id: '002', title: '睡觉', done: true},
        {id: '003', title: '打游戏', done: false}
      ]
    }
  }
</script>

<TodoItem v-for="item in todos" :key="todos.id" :item="item"/>
<script>
props:["todos"]
</script>

以下修改为$emit 父组件给子组件c
-------
<TodoHeader @addTodo="addTodo"/>
<script>
methods: {
            // 增加
            addTodo(itemObj) {
                this.todos.unshift(itemObj);
            }
}
</script>

<script>
<template>
  <div class="header">
    <input type="text" v-model.trim="addTodoTitle" placeholder="请输入待办事项,按回车键" @keyup.enter="add($event)"/>
  </div>
</template>
methods: {
    add(event) {
      if (this.addTodoTitle != '') {
        const itemObj = {id: nanoid(), title: this.addTodoTitle, done: false};
        this.$emit('addTodo',itemObj);
      }
    }
  }
</script>

子组件给父组件传值

<TodoHeader :addTodo="addTodo"/>
methods: {
    addTodo(itemObj) {
      this.todos.unshift(itemObj);
    }
  }

<input type="text" v-model.trim="addTodoTitle" placeholder="请输入待办事项,按回车键" @keyup.enter="add($event)"/>
<script>
props: ['addTodo'],
methods: {
    add(event) {
      if (this.addTodoTitle != '') {
        const itemObj = {id: nanoid(), title: this.addTodoTitle, done: false};
        this.addTodo(itemObj);
      }
    }
  }
</script>

⚠️【data,props,methods,computed】不要有相同的名称!

组件任意传值

beforeCreate() {
        Vue.prototype.$bus = this
    }

----------------------------------------------------------------
methods: {
            // 勾选或取消
            changeTodo(id) {
                this.todos.forEach((item) => {
                    if (item.id == id) {
                        item.done = !item.done;
                    }
                })
            }
            ,
            deleteTodo(id) {
                this.todos = this.todos.filter((item) => {
                    return item.id != id;
                })
            }
        }
mounted() {
            this.$bus.$on('changeTodo',this.changeTodo);
            this.$bus.$on('deleteTodo',this.deleteTodo);
        },
        beforeDestroy() {
            this.$bus.$off('changeTodo');
            this.$bus.$off('deleteTodo');
        }

----------------------------------------------------------------
methods: {
            doneChanged(id) {
                this.$bus.$emit('changeTodo',id);
            },
            delItem(id) {
                this.$bus.$emit('deleteTodo',id);
            }
        },

点击复选框值改变

暂时使用函数,父组件->子组件

🛑不推荐使用v-model:checked=“xxx”,如果xxx是对象里面的值,可以,如果不是,vue不允许。因为vue监视的对象的改变的机制

v-model绑定的值不能是传过来的值,props的值是不能修改的

App.vue
	TodoList.vue
		TodoItem.vue

App 传递给子组件
<TodoList :todos="todos" :changeTodo="changeTodo"/>
<script>
    data() {
            return {
                todos: [
                    {id: '001', title: '吃饭', done: false},
                    {id: '002', title: '睡觉', done: true},
                    {id: '003', title: '打游戏', done: false}
                ]
            }
        },
        methods: {
            // 勾选或取消
            changeTodo(id) {
                this.todos.forEach((item) => {
                    if (item.id == id) {
                        item.done = !item.done;
                    }
                })
            }
        }
</script>

TodoList props接收,传递给子组件
<TodoItem v-for="item in todos" :key="todos.id" :item="item" :changeTodo="changeTodo"/>
<script>
props:["changeTodo"]
</script>

TodoItem
<input type="checkbox" v-model:checked="item.done" @change="doneChanged(item.id)"/>
<script>
    export default {
        name: "TodoItem",
        methods: {
                doneChanged(id) {
                    this.changeTodo(id);
                }
        },
        props: ['changeTodo']
    }
</script>

组件自定义事件

子组件向父组件传值

$emit实现
通过父组件向子组件绑定一个自定义事件,等子组件触发这个事件,就触发getValue函数
<TodoFooter v-on:customize="getValue"/>
<!--简写-->
<TodoFooter @customize="getValue"/>
<!--只触发一次 <TodoFooter v-on:customize.once="getValue"/> -->
<script>
    data() {
            return {
                context: [
                    {completed: '', unfinished: ''}
                ]
            }
        },
methods: {
            getValue(completed, unfinished) {
                console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
            }
        }
</script>

<button @click="fromFather()">from父组件传值</button>
<script>
methods:{
    fromFather() {
                this.$emit('customize',this.doneTotle, this.todos.length)
            }
}
</script>
ref实现
<TodoFooter ref="inputValue"/>
<script>
methods: {
            getValue(completed, unfinished) {
                console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
},
mounted() {
            setTimeout(()=>{
                this.$refs.inputValue.$on('customize', this.getValue);
            },2000)//只触发一次
            //this.$refs.inputValue.$once('customize', this.getValue);
}
</script>

<button @click="fromFather()">from父组件传值</button>
<script>
methods:{
    fromFather() {
          this.$emit('customize',this.doneTotle, this.todos.length)
    }
}
</script>

解绑自定义事件

//解绑单个自定义事件
this.$off('customize')
//解绑多个自定义事件
this.$off(['xxx','xxx'])
//解绑所有的自定义事件
this.$off()

子组件向父组件传递的值显示在页面上

赋值给data,使用插值语法显示到页面上

<h2>{{context[0].completed}}{{context[0].unfinished}}</h2>
<TodoFooter :todos="todos"
            v-on:customize="getValue" ref="inputValue"/>
<script>
data() {
            return {
                todos: [
                    {id: '001', title: '吃饭', done: false},
                    {id: '002', title: '睡觉', done: true},
                    {id: '003', title: '打游戏', done: false}
                ],
                context: [
                    {completed: '', unfinished: ''}
                ]
            }
        }
        ,
        methods: {
            getValue(completed, unfinished) {
                console.log('已完成数量:' + completed, '未完成数量:' + unfinished)
                this.context[0].completed = completed;
                this.context[0].unfinished = unfinished;
            }
        }
        ,
        mounted() {
            setTimeout(() => {
                this.$refs.inputValue.$on('customize', this.getValue);
            }, 2000)
        }
</script>

⚠️注意:回调函数简写时this指向

this.$refs.inputValue.$on('customize', function(completed, unfinished){
 this的指向是触发事件的实例对象,谁触发customize就是谁
});
this.$refs.inputValue.$on('customize',(completed, unfinished)=>{
 this的指向是外面的组件实例对象
});
绑定原生DOM事件
<Student> @click.native="xxx" </Student>

全局事件总线

组件间通信方式,适用于任意组件间通信

安装

new Vue({
    beforeCreate(){
		Vue.prototype.$bus = this;	
    }
})

使用

(1)接收数据:A组件想接收数据,则A组件中的$bus绑定自定义事件,事件的回调留在A组件自身

methods(){
    demo(data){}
}
mounted(){
    this.$bus.$on('xxx',this.demo)
}

(2)提供数据:this.$bus.$emit('xxx',数据)

最好在beforeDestroy钩子中,用$off解绑当前组件所用到的事件

消息订阅与发布

组件间通信方式,适用于任意组件间通信

使用步骤

1.安装:pubsub npm i pubsub.js

2.引入:import pubsub from 'pubsub.js'

3.接收数据:A组件想接收数据,则A组件中订阅消息,订阅的回调留在A组件本身

methods(){
    demo(data){}
    mounted(){
        this.pid = pubsub.subscribe('xxx',this.demo)//订阅消息,可改为箭头函数,使用funtion(){}匿名函数的this作用域是unfiend
    }
}

4.提供数据:pubsub.publish('xxx',数据)

5.最好在beforeDestory钩子中,用pubsub.unsubscribe(pid)取消订阅

$nextTick

将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法

<input v-show="item.isEdit" type="text" v-model="item.title" @blur="alterTitle($event,item)" ref="inputTitle">

editItem(item) {
                if (item.hasOwnProperty('isEdit')) {
                    item.isEdit = true;
                }
                this.$set(item, 'isEdit', true)
                this.$nextTick(function () {
                    this.$refs.inputTitle.focus();
                });
            }

过渡与动画

image-20220207210255578

用法

1.准备样式

元素进入的样式
  1. v-enter:进入的起点
  2. v-enter-active:进入过程中
  3. v-enter-to:进入的终点
元素离开的样式
  1. v-leave:离开的起点
  2. v-leave-active:离开的过程中
  3. v-leave-to:离开的终点

2.使用<transition>包裹要过渡的元素,并配置name属性

<transition name="hello">
    <h1 v-show="isShow">
        你好啊!
    </h1>
</transition>

3.若有多个元素需要过渡,则需要使用<transition-group>,且每个元素都要指定key值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

折腾的小飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值