失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【VUE3】保姆级基础讲解(二)计算属性 vue组件 vue-cli脚手架 组件通讯 插槽slot

【VUE3】保姆级基础讲解(二)计算属性 vue组件 vue-cli脚手架 组件通讯 插槽slot

时间:2021-03-29 01:54:00

相关推荐

【VUE3】保姆级基础讲解(二)计算属性 vue组件 vue-cli脚手架 组件通讯 插槽slot

目录

计算属性computed

侦听器watch

对象监听

组件

注册全局组件

注册局部组件

Vue-CLI脚手架

安装和使用

.browserslistrc

main.js

jsconfig.json

组件通讯

父组件传递给子组件

props基础

非prop的attribute

子组件传递给父组件

插槽slot

基础使用

具名插槽

动态插槽名

作用域插槽

计算属性computed

如果我得到了socre,希望根据这个判断是否及格,可以这么做:

<div>{{score>=60?'及格':'不及格'}}</div>

但是这么做的弊端是,需要在插值语法中放置过于复杂的逻辑,不易于维护

有一种方法是在methods中放置函数:

<body><div class="app"><div>{{gett()}}</div></div><script src="./lib/vue.js"></script><script>const app = Vue.createApp({data:function(){return {score:80}},methods:{gett(){return this.score>=60?'及格':'不及格'}}})app.mount(".app")</script></body>

但是我们希望不要调用函数,而是直接用变量名,就可以使用计算属性computed

对于任何包含响应式数据的复杂逻辑,都应该使用计算属性

<body><div class="app"><!-- 在调用时直接写函数的名字,而不用写() --><div>{{gett}}</div></div><script src="./lib/vue.js"></script><script>const app = Vue.createApp({data:function(){return {score:80}},computed:{gett(){return this.score>=60?'及格':'不及格'}}})app.mount(".app")</script></body>

在调用时直接写函数的名字,而不用写()

计算属性的优势

计算属性直接放函数名字,比较简洁计算属性具有缓存

<div class="app"><div>{{gett}}</div><div>{{gett}}</div><div>{{gett}}</div><div>{{gett1()}}</div><div>{{gett1()}}</div><div>{{gett1()}}</div></div>

methods:{gett1(){console.log('11--------11');return this.score>=60?'及格':'不及格'}},computed:{gett(){console.log('22--------22');return this.score>=60?'及格':'不及格'}}

结果是

也就是说计算属性执行一次后会将结构放在缓存中,下次再使用时不用重新调用

侦听器watch

可以侦听数据的改变

<body><div class="app"><div>{{gett}}</div><button @click="change"></button></div><script src="./lib/vue.js"></script><script>const app = Vue.createApp({data:function(){return {score:80}},methods:{change(){this.score = 90}},watch:{score(newvalue,old){console.log('score has been changed');console.log('new:'+newvalue);console.log('old:'+old);}}})app.mount(".app")</script></body>

watch里用变量的名字定义了一个函数,定义了这个变量发生变化时,执行哪些操作

这个函数可以调用此变量的新旧值

对象监听

这个方法对于对象也可以监听,但是只能监听对象整体赋值

methods:{change(){this.score = {name:'1232'};}},watch:{score(newvalue,old){console.log('score has been changed');},

上面这个可以监听到

methods:{change(){this.score.name = '1233'}},watch:{score(newvalue,old){console.log('score has been changed');},

上面这个不行,因为watch默认不会进行深度监听、

如果要进行深度监听:

watch:{score:{handler(newvalue,oldvalue){console.log('score has been changed')},deep:true}}

如果在最开始渲染界面时就想运行这个监听函数,使用immediate:true

watch:{score:{handler(newvalue,oldvalue){console.log('score has been changed')},deep:true,immediate:true}}

做一个小案例

<!DOCTYPE html><html lang="en"><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"><title>Document</title><style>* {margin: 0;padding: 0}ul{width: -moz-fit-content;width: fit-content;}li {display: inline-block;list-style-type: none;width: 100px;height: 40px;border: 1px solid black;text-align: center;}.title li {background-color: gray;}.red{color: red;}</style></head><body><div class="app"><ul class='title'><li>序号</li><li>书籍名称</li><li>出版日期</li><li>价格</li><li>购买数量</li><li>操作</li></ul><ul v-for="(item,index) in products" @click="light(index)" :class="{red:index==indexchoice}"><li>{{index+1}}</li><li v-for="(value,key) in item">{{value}}</li><li><button @click="sub(index)">-</button>{{counter[index]}}<button @click="add(index)">+</button></li><li><button @click='deleteProduct(index)'>移除</button></li></ul><h1>总价:¥{{sum}}</h1><h1>{{alert}}</h1></div><script src="./lib/vue.js"></script><script>const app = Vue.createApp({data: function () {return {counter: [],alert: '',indexchoice:-1,products: [{ name: '《算法导论》', data: '-9', price: 85 }, { name: '《代码大全》', data: '-9', price: 25 }, { name: '《编程》', data: '-9', price: 85 }]}},methods: {sub(index) {this.counter[index] = this.counter[index] > 1 ? this.counter[index] - 1 : 1},add(index) {this.counter[index]++},deleteProduct(index) {this.products.splice(index, 1);this.counter.splice(index, 1);},light(index){this.indexchoice = index}},computed: {sum() {let summ = 0for (let i = 0; i < this.products.length; i++) {summ += this.products[i].price * this.counter[i]}if (summ == 0) {this.alert = '购物车为空'}else { this.alert = '' }return summ}},beforeMount() {for (let i = 0; i < this.products.length; i++) {this.counter.push(1)}}})app.mount(".app")</script></body></html></html>

组件

注册全局组件

这里分三步创造全局组件product-item,在使用时直接使用即可使用模板内容

<body><div class="app"><product-item></product-item><product-item></product-item><product-item></product-item></div><script src="./lib/vue.js"></script><script>//1.组件:App组件(根组件)const App = {}//2.创建appconst app = Vue.createApp(App)//3.注册一个全局组件ponent('product-item', {template: `<div><h2>商品</h2><div>图片</div><h3>价格</h3></div>`})app.mount(".app")</script></body>

template写在这里感觉不是很方便,也可以写在上面再用id引入,并且在app中,之前所有的方法一样适用

当然也可以注册多个全局组件

<body><div class="app"><product-item></product-item><product-item></product-item><product-item></product-item></div><template id="products"><div><h2>{{name}}</h2><div>图片</div><h3>价格</h3></div><button @click="fn">hhh</button></template><template id="navs"><h2>hhhh</h2></template><script src="./lib/vue.js"></script><script>//1.组件:App组件(根组件)const App = {}//2.创建appconst app = Vue.createApp(App)//3.注册一个全局组件ponent('product-item', {template: '#products',data(){return{name:'aaaa'}},methods:{fn(){console.log(111);}}})ponent('nav-item',{template:"#navs"})app.mount(".app")</script></body>

这里定义了两个全局组件 product-item 和 nav-item

全局组件可以在任意组件的template中使用

例如下面这种写法是允许的:

<template id="navs"><product-item></product-item><h2>hhhh</h2></template>

注册局部组件

一般开发时很少注册全局组件

局部组件是在app中采用components api设置:

<body><div class="app"><product-item></product-item><nav-item></nav-item></div><template id="products"><div><h2>{{name}}</h2><div>图片</div><h3>价格</h3></div></template><template id="navs"><h2>hhhh</h2></template><script src="./lib/vue.js"></script><script>const app = Vue.createApp({data(){return{}},methods:{},components:{'product-item':{template:'#products',data(){return{name:'hhhh'}}},'nav-item':{template:'#navs'}}})app.mount(".app")</script></body>

局部组件不可以在任意组件的template中使用

Vue-CLI脚手架

但是上述的开发模式很繁琐,将js vue css html都写在一起

一般使用脚手架搭建项目模板

安装和使用

在命令行:

npm i @vue/cli -g

使用脚手架创造项目,这种方法基于webpack

vue create project_name

会问你一堆问题,具体什么意思这里不赘述了

创建好之后可以运行项目

npm run serve

这里还有第二种方法创建vue项目,这个是基于vite工具

npm init vue@latest

其过程是:

安装一个本地工具:create-vue使用create-vue创建项目

.browserslistrc

这里解释一下.browserslistrc

> 1%last 2 versionsnot deadnot ie 11

只适配市场占有率大于百分之1的浏览器,通过 caniuse 网站适配浏览器的最后两个版本不适配 没有维护 的浏览器不适配 ie 11

main.js

在src文件夹下有main.js文件

import { createApp } from 'vue/dist/vue.esm-bundler'import App from './App.vue'createApp(App).mount('#app')

这里说的是在App.vue中引入App组件并渲染

说一下下来两者的区别

import { createApp } from 'vue'import { createApp } from 'vue/dist/vue.esm-bundler'

第一个是 runtime 模式

第二个是 runtime+compile 模式

具体是体现在<template>的编译模式,如果直接在 main.js文件中书写App,则在编译<template>时需要用到源码的 compile ,所以必须用第二种

如果在.vue文件中书写App,webpack在打包的时候就会直接编译,不会用到VUE源码,所有第一个就可以

那么在App.vue中,由三部分组成,即

<template>

<script>

<style>

这里使用一个最简单的例子

<template><h2>{{name}}</h2></template><script>export default{data(){return{name:'hhhh'}},methods:{}}</script><style></style>

代码书写逻辑跟之前书写的一样

这里需要注意的是style,当我们设置了一个样式,我们希望这个样式只在这个组件内生效,也就是说这个样式只在这个 .vue文件生效,即有自己的作用域,那么可以:

<style scoped></style>

jsconfig.json

jsconfig.json文件是给VScode的,为了让它有更加友好的提示

例如path:

"paths": {"@/*": ["src/*"],"utils/*":["src/utils/*"]},

在打 utils时就会自动提示

jsconfig.json的作用_我叫火柴的博客-CSDN博客_jsconfig

组件通讯

一般的项目开发中会出现很多的 组件嵌套

即在一个组件定义中应用另外一个组件

导入方式:

<template><Infos></Infos></template><script>import Infos from './components/infos.vue'export default{components: { Infos }}</script><style scoped></style>

如果存在嵌套,那么就需要进行数据通讯

父组件传递给子组件:采用 props属性

子组件传递给父组件:采用$emit触发事件

父组件传递给子组件

props基础

子:

<template><div class="infos"><h1>{{name}}</h1><h2>{{age}}</h2></div></template><script>export default{//定义props有两种方式,数组和对象方式// props:['name','age']props:{name:{type:String,default:'nameeee'},age:{type:Number,default:18}}}</script><style scoped></style>

父组件:

<template><Infos name='kobe' :age='18'></Infos></template><script>import Infos from './components/infos.vue'export default{components: { Infos }}</script><style scoped></style>

这里父组件引入子组件,并通过属性的方式给子组件传递数据

子组件在 api中通过props属性添加变量名

props的设定有两种方法,数组法和对象法,对象法可以设定数据想要的数据类型和默认值,显然是更优秀的方法

这里有一个需要注意的,如果使用对象法,数据类型为对象或者数组,那么其默认值要通过函数来设置

frends:{type:Object,default:()=>({name:'111',age:19})},frends1:{type:Array,default:()=>(['aaa','bbb'])}

非prop的attribute

也就是父组件传递给子组件一些数据,但是在子组件中的Props属性中没有进行设置

例如下面的这个class就是非prop的attribute

<Infos name='kobe' :age='18' class="abc"></Infos>

此属性会默认添加到子组件的根元素上

也就是说在子组件的<div class="infos"> 中会添加 class='abc' 属性

当然如果你不希望这么做,可以直接禁止,在子组件中设置:

inheritAttrs:false,props:{}

如果我希望拿到这个属性,赋予给其他的元素,而不是根元素,那么在子组件的此元素里应该:

<div class="infos"><h1 :class='$attrs.class'>{{name}}</h1><h2>{{age}}</h2></div>

那么h1就可以拿到传进来的 class属性

子组件传递给父组件

这个通过一个小案例说明

在子组件中设置两个按钮,分布为 +1 +5按钮

<template><div class="infos"><button @click='addnum(1)'>+1</button><button @click='addnum(5)'>+5</button></div></template><script>export default{methods:{addnum(count){this.$emit('addnumber',count)}}}</script><style scoped></style>

这里给按钮设置监听函数 addnum()

在这个函数里面用this.$emit向父组件发送监听名字addnumber(自己设置的)和参数 count

然后在父组件中:

<template><h1>{{counter}}</h1><Infos @addnumber='addcount'></Infos></template><script>import Infos from './components/infos.vue'export default{data(){return{counter:0}},components: { Infos },methods:{addcount(count){this.counter = this.counter+count}}}</script><style scoped></style>

给用到的子组件添加@addnumber='addcount'这个监听名字是之前在子组件中自己设置的,然后使用addcount函数在父组件中执行想要的逻辑

一般来说子组件可能会要发送很多监听名字,这里可以用emits方法进行说明,也方便他人查看

export default{emits:['addnumber'],methods:{addnum(count){this.$emit('addnumber',count)}}}

插槽slot

对于这样一个导航栏,其实本质上都是三部分组成,既左边-中间-右边三部分

但是又各有不同,比如中间部分,有的是 搜索框 有的是 购物车几个字

那么在创建组件时,就可以使用插槽slot,将三个部分预留出来,后期再填入想要的内容

基础使用

子组件

这里使用slot作为插槽,当父组件没有传入元素时,会展示默认元素

<template><slot><p>我是默认内容</p></slot></template>

父组件

直接在调用的子组件下面写自己想要的元素

<template><Page :index="index"><h1>hhhh</h1></Page><Page></Page></template>

具名插槽

如果存在多个slot,就需要设定名字

子组件

<template><div><slot name="one"></slot></div><div><slot name="two"></slot></div></template>

父组件

使用v-slot:xxx设定对应slot的名字

<template><Page><template v-slot:one><h1>hhhhh</h1></template><template v-slot:two><h1>wwwwww</h1></template></Page></template>

缩写:

<template #one><h1>hhhhh</h1></template>

动态插槽名

采用v-slot:[xxx]的方式将插槽绑定到变量xxx中

<template v-slot:[choose]><h1>hhhhwwh</h1></template>

作用域插槽

也就是通过插槽将子组件的变量传到父组件

子组件

<template><div><slot name="one" :item="item" :age="age"><h1>我是默认值</h1></slot></div></template><script>export default {data(){return{item:'hhh',age:18}},}</script><style scoped></style>

itemage是定义在子组件中的两个变量,其作用域只在子组件

在slot中添加:item="item" :age="age"将两个变量传递出去

父组件

使用#one="props" 拿到传递进来的变量,这个props是自己设置的名字,其本质是对象,包含了传递进的变量

<Page><template #one="props"><h1>{{props.item}}</h1><h2>{{props.age}}</h2></template></Page>

这里通过一个案例说明作用域插槽的一般使用

子组件:

这里子组件使用父组件传进来的products创建了n个div,每个div里面都有一个slot,其名字都是one

但是每个slot具有不同的item,并将其传递给父组件

<template><div v-for="item in products"><slot name="one" :item="item"><span>{{item}}</span></slot></div></template><script>export default {props:['products']}</script><style scoped></style>

父组件

使用slot传递进来的item创建元素替代slot

<template><Page :products="products"><template #one="props"><button>{{props.item}}</button></template></Page><Page :products="products"><template #one="props"><a href="">{{props.item}}</a></template></Page></template><script>import Page from './components/page.vue'export default {data(){return{products:['shoe','clothes','skirts']}},components:{Page}, }</script><style scoped></style>

可以实现改变元素种类,保持内容不变

如果觉得《【VUE3】保姆级基础讲解(二)计算属性 vue组件 vue-cli脚手架 组件通讯 插槽slot》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。