1.小兔鮮
所有都折疊 ctrl + k,ctrl+0
所有都展開 ctrl + k,ctrl+j
當前結構渲染5次
<BaseBrandItem v-for="item in 5" :key="item"><BaseBrandItem>
2.scoped樣式沖突
- 結構:只能有一個根元素
- 樣式:全局樣式(默認):影響所有組件
局部樣式:scoped下樣式,只作用于當前組件 - 邏輯:el根實例獨有,data是一個函數,其他配置項一致
scoped原理:
- 給當前組件模板的所有元素,都會被添加上一個自定義屬性 data-v-hash值,不同的hash值區分不同的組件
- css選擇器后面,被自動處理,添加上了屬性選擇器 div[data-v-5f6a9d56]
3. data是一個函數
組件的data選項必須是一個函數
每次創建新的組件實例,都會新執行一次data函數,得到一個新對象
export default {data() {return {count: 100,}}}
4.組件通信
組件與組件之間的數據傳遞,想用其他組件的數據=>組件通信
組件關系:父子關系和非父子關系
組件通信解決方案:
- 父子關系:props和$emit
- 非父子關系:
provide&inject
eventbus
通用解決方案:Vuex(適合復雜業務場景)
父子關系:
父傳子
props后面的屬性名必須與子組件里面的屬性名一致
子傳父
父組件對消息進行監聽,@后面的名字與子組件中$emit中的第一個名字一樣 @changeTitle=“handleChange”,handleChange是父組件的處理函數
子組件
<script>
export default {name: 'Son-Child',props: ['title'],methods: {changeFn() {// 通過this.$emit() 向父組件發送通知this.$emit('changTitle','傳智教育')},},
}
</script>
父組件
<!-- 2.父組件對子組件的消息進行監聽 --><Son :title="myTitle" @changTitle="handleChange"></Son></div>
</template><script>
import Son from './components/Son.vue'
export default {name: 'App',data() {return {myTitle: '學前端,就來黑馬程序員',}},components: {Son,},methods: {// 3.提供處理函數,提供邏輯handleChange(newTitle) {this.myTitle = newTitle},},
}
</script>
5.props詳解
prop是組件上注冊的一些自定義屬性
作用:向子組件傳遞數據
可以傳遞任意數量,任意類型的prop
把hobby對象里面的標點符號改為、
{{hobby.join(‘、’)}}
子組件
<template><div class="userinfo"><h3>我是個人信息組件</h3><div>姓名:{{username}}</div><div>年齡:{{age}}</div><div>是否單身:{{isSingle}}</div><div>座駕:{{car.brand}}</div><div>興趣愛好:{{hobby.join('、')}}</div></div>
</template><script>
export default {props:['username','age','isSingle','car','hobby']
}
</script>
父組件
export default {data() {return {username: '小帥',age: 28,isSingle: true,car: {brand: '寶馬',},hobby: ['籃球', '足球', '羽毛球'],}},components: {UserInfo,},
}
props校驗
為組件的prop指定驗證要求,不符合要求,控制臺有錯誤提示
語法:
- 類型校驗:
props: {校驗的屬性名:類型 //String Number Boolean Array Object Function
}
- 非空校驗、默認值、自定義校驗
// 完整寫法(類型、默認值、非空、自定義校驗)props: {w: {type: Number,//類型required: true,//是否必填default: 0, //默認值validator(val) {//自定義校驗邏輯if (val >= 100 || val <= 0) {console.error('傳入的范圍必須是0-100之間')return false} else {return true}},},},
props vs data
共同點:都可以給組件提供數據
不同點:
data:數據是自己的,隨便改
props:數據是外部的,不能直接改,要遵循單項數據流
如:prop傳的count不能在結構里寫++,或–,只能在methods里面寫加或減函數,函數里用子傳父$emit,用count+1,不能寫++
父組件
一旦老爹改了,視圖也跟著改了
$emit通知父組件要修改,其prop更新,會單項向下流動,影響到子組件
methods:{handleChange(newVal){// console.log(newVal);this.count = newVal}}
6.小黑記事本-組件版
渲染功能:
1.提供數據: 提供在公共的父組件 App.vue
2.通過父傳子,將數據傳遞給TodoMain
3.利用 v-for渲染
添加功能:
1.手機表單數據 v-model
2.監聽事件(回車+點擊都要添加)
3.子傳父,講任務名稱傳遞給父組件 App.vue
4.進行添加 unshift(自己的數據自己負責)
5.清空文本框輸入的內容
6.對輸入的空數據 進行判斷
刪除功能
1.監聽事件(監聽刪除的點擊) 攜帶id
2.子傳父,講刪除的id傳遞給父組件的App.vue
3.進行刪除filter(自己的數據 自己負責)
底部合計:父傳子 傳list 渲染
清空功能:子傳父 通知父組件 → 父組件進行更新
持久化存儲:watch深度監視list的變化 -> 往本地存儲 ->進入頁面優先讀取本地數據
根組件
<template><!-- 主體區域 --><section id="app"><!--導入,注冊,使用--><ToDoHeader @add="handleAdd"></ToDoHeader><ToDoMain @del="handleDel" :list="list"></ToDoMain><ToDoFooter @clear="handleClear" :list="list"></ToDoFooter></section>
</template><script>
import ToDoHeader from './components/ToDoHeader.vue';
import ToDoMain from './components/ToDoMain.vue';
import ToDoFooter from './components/ToDoFooter.vue';
export default {data () {return {list: JSON.parse(localStorage.getItem('list')) || [{id: 1, name:'打籃球'},{id: 2, name: '游泳'},]}},components: {ToDoHeader,ToDoMain,ToDoFooter},watch: {list: {deep: true,handler(newVal) {localStorage.setItem('list', JSON.stringify(newVal))},},},methods: {handleAdd(Todoname) {this.list.unshift({id: +new Date ,name: Todoname})},handleDel(id) {this.list = this.list.filter((item)=> item.id !== id)},handleClear() {this.list = []}},}
</script>
7.非父子通信-event bus 事件總線
- 創建一個都能訪問到的事件總線(空vue實例)
import Vue from 'vue'
const Bus = new Vue()
export default Bus
- A組件(接收方),監聽Bus實例的事件
created() {Bus.$on('sendMsg', (msg) => {// console.log(msg)this.msg = msg})},
- B組件(發送方),觸發Bus實例的事件
methods: {sendMsgFn() {Bus.$emit('sendMsg', '今天天氣不錯,適合旅游')},},
8.非父子通信-provide&inject
作用:跨層級共享數據
根組件:
provide() {return {// 簡單類型 是非響應式的//拿的是下面data的數據color: this.color,// 復雜類型 是響應式的userInfo: this.userInfo,}},data() {return {color: 'pink',userInfo: {name: 'zs',age: 18,},}},
子組件:
用inject獲取元素后,直接用雙括號渲染
<template><div class="grandSon">我是GrandSon{{ color }} -{{ userInfo.name }} -{{ userInfo.age }}</div>
</template><script>
export default {inject: ['color', 'userInfo'],
}
</script>
9.v-model原理,詳解
是value屬性和input屬性的和寫,提供數據的雙向綁定
數據變,視圖變:value
視圖變,數據變@input
注意:$event用于在模板中,獲取時間的形參
表單類組件封裝&v-model簡化代碼
- 表單類組件封裝
這就是data()里面的數據可以改,props里面獲取到的屬性不能改,那么v-model就無法綁定props里面的值了的問題,因為表單提交,父組件,子組件不能改動,所以要吧v-model進行拆解。
子組件
<template><div><select :selectId="selectId" @change="changeCity"><option value="101">北京</option><option value="102">上海</option><option value="103">武漢</option><option value="104">廣州</option><option value="105">深圳</option></select></div>
</template>
<script>
export default {props: {selectId: String},methods: {//子傳父selectCity(e) {this.$emit('changeCity',e.target.value )}}
}
</script>
- 父組件v-model簡化代碼,實現子組件和父組件雙向綁定
子組件:props通過value接收,事件觸發input
父組件中:v-model給組件中直接綁數據(:value+@input)
父組件
<template><div class="app"><BaseSelectv-model="selectId"></BaseSelect></div>
</template>
<script>
import BaseSelect from './components/BaseSelect.vue'
export default {data() {return {selectId: '102'}},components: {BaseSelect,},
}
</script>
10. .sync修飾符
作用:可以實現子組件與父組件數據的雙向綁定,簡化代碼
特點:prop屬性,可以自定義,非固定為value
場景:封裝彈框類的基礎組件,visible屬性true顯示false隱藏
本質:就是 :屬性名 和@update:屬性名 合寫
父組件
<template><div class="app"><button @click="openDialog">退出按鈕</button><!-- isShow.sync => :isShow="isShow" @update:isShow="isShow=$event" --><BaseDialog :isShow.sync="isShow"></BaseDialog></div>
</template>
<script>
import BaseDialog from './components/BaseDialog.vue'
export default {data() {return {isShow: false,}},methods: {openDialog() {this.isSHow = true}},// console.log(document.querySelectorAll('.box')); components: {BaseDialog,},
}
</script>
11.ref 和$refs
作用:
- 獲取dom元素
- 組件實例
特點:查找范圍=>當前組件內(更精確穩定)
獲取dom元素目標標簽-添加ref屬性
<div ref="chartRef">我是渲染圖標的容器</div>恰當時機,通過this.$refs.xxx獲取目標標簽
mounted() {console.log(this.$refs.chartRef)var myChart = echarts.init(this.$refs.chartRef)
}
組件實例目標組件-添加ref屬性
<BaseForm ref="baseForm"></BaseForm>恰當時機,通過this.$refs.xxx獲取目標組件,就可以調用組件對象里面的方法。this.$refs.baseForm.組件方法()
12.vue異步更新和$nextTick
由于vue異步更新,我們需要用到$nextTick,等dom更新完后才會觸發此方法里的函數體。
語法
this.$nextTick(()=>{this.$refs.inp.focus()
})
methods: {editFn() {// 1.顯示文本框this.isShowEdit = true// 2.讓文本框聚焦 (會等dom更新完之后 立馬執行nextTick中的回調函數)this.$nextTick(() => {console.log(this.$refs.inp)this.$refs.inp.focus()})
setTimeOut也可以,但是$nextTick更精準
13.自定義指令
自己定義的指令,可以封裝一些dom操作,擴展額外功能
全局注冊指令-在mian.js
v-指令名(focus) ,寫在app.vue里
// 1. 全局注冊指令
Vue.directive('focus', {// inserted 會在 指令所在的元素,被插入到頁面中時觸發inserted (el) {// el 就是指令所綁定的元素// console.log(el);el.focus()}
})
局部指令
// 2. 局部注冊指令directives: {// 指令名:指令的配置項focus: {inserted (el) {el.focus()}}}
14.自定義指令的值
實現一個color指令,傳入不同的顏色,給標簽設置文字顏色
- 用‘等號’綁定具體的參數值
- 通過binding.value可以拿到指令值,指令值修改會觸發update函數
<div v-color="color"</div>
directives: {color: {// 1. inserted 提供的是元素被添加到頁面中時的邏輯inserted (el, binding) {// binding.value 就是指令的值el.style.color = binding.value},// 2. update 指令的值修改的時候觸發,提供值變化后,dom更新的邏輯update (el, binding) {el.style.color = binding.value}}}
<template><div><h1 v-color="color1">指令的值1測試</h1><h1 v-color="color2">指令的值2測試</h1></div>
</template><script>
export default {data () {return {color1: 'red',color2: 'orange'}},directives: {color: {// 1. inserted 提供的是元素被添加到頁面中時的邏輯inserted (el, binding) {// console.log(el, binding.value);// binding.value 就是指令的值el.style.color = binding.value},// 2. update 指令的值修改的時候觸發,提供值變化后,dom更新的邏輯update (el, binding) {console.log('指令的值修改了');el.style.color = binding.value}}}
}
</script>
15.自定義指令-v-loading指令封裝
使用偽元素定位,而不是新加一個標簽,會更方便,只需添加移除類即可
- inserted,設置默認狀態
- update,更新類名狀態
<template><div class="main"><div class="box" v-loading="isLoading"><ul><li v-for="item in list" :key="item.id" class="news"><div class="left"><div class="title">{{ item.title }}</div><div class="info"><span>{{ item.source }}</span><span>{{ item.time }}</span></div></div><div class="right"><img :src="item.img" alt=""></div></li></ul></div><div class="box2" v-loading="isLoading2"></div></div>
</template><script>
// 安裝axios => yarn add axios
import axios from 'axios'// 接口地址:http://hmajax.itheima.net/api/news
// 請求方式:get
export default {data () {return {list: [],isLoading: true,isLoading2: true,}},async created () {// 1. 發送請求獲取數據const res = await axios.get('http://hmajax.itheima.net/api/news')setTimeout(() => {// 2. 更新到 list 中,用于頁面渲染 v-forthis.list = res.data.data//結束后this.isLoading = false}, 2000)},directives: {loading: {inserted (el, binding) {binding.value ? el.classList.add('loading') : el.classList.remove('loading')},//當數據變化時做出改動update (el, binding) {binding.value ? el.classList.add('loading') : el.classList.remove('loading')}}}
}
</script><style>
.loading:before {content: '';position: absolute;left: 0;top: 0;width: 100%;height: 100%;background: #fff url('./loading.gif') no-repeat center;
}
16.插槽-默認插槽
插槽分為兩類:默認插槽(定制一處結構)+具名插槽(定制多處結構)
讓自己內部的一些結構支持自定義
<!-- 1. 在需要定制的位置,使用slot占位,寫在子組件 --><slot></slot>
<!-- 2. 在使用組件時,組件標簽內填入內容,寫在app.vue根組件 --><MyDialog><div>你確認要刪除么</div></MyDialog>
插槽-后備內容(默認值)
在slot標簽內,放置內容,作為默認顯示內容
使用Mydialog時,里面不傳內容,則會傳slot里面的。
若是有內容,則Mydialog替換slot里面的
17.插槽-具名插槽
為了使用多個插槽
語法:
- 多個slot使用name屬性區分名字
子組件
<div class="dialog-header"><!-- 一旦插槽起了名字,就是具名插槽,只支持定向分發 --><slot name="head"></slot></div><div class="dialog-content"><slot name="content"></slot></div><div class="dialog-footer"><slot name="footer"></slot></div>
- template配合v-slot:名字 來對應 (定向)分發標簽
根組件
<MyDialog><!-- 需要通過template標簽包裹需要分發的結構,包成一個整體 --><template v-slot:head><div>我是大標題</div></template><template v-slot:content><div>我是內容</div></template><template #footer><button>取消</button><button>確認</button></template></MyDialog>
- v-slot:插槽名可以簡化為 #插槽名
<template #footer><button>取消</button><button>確認</button></template>
插槽-作用域插槽-插槽的傳參語法
作用:定義slot插槽的同時可以傳值,給插槽上綁定數據,將來使用組件時可以用。
場景:封裝表格組件
- 給slot標簽以添加屬性方式傳值
<!-- 1. 給slot標簽,添加屬性的方式傳值 --><slot :row="item" msg="測試文本"></slot>
- 所有添加屬性,都會被收集到一個對象中
{ id: 2, msg: '測試文本' }
- 在template中通過 #插槽名="obj"接收,默認插槽名為default
<MyTable :data="list"><!-- 3. 通過template #插槽名="變量名" 接收 --><template #default="obj"><button @click="del(obj.row.id)">刪除</button></template></MyTable>
整體效果
根組件
<template><div><MyTable :data="list"><template #default="obj"><button @click="del(obj.row.id)">刪除</button></template></MyTable><MyTable :data="list2"><template #default="{row}"><button @click="show(row)">查看</button></template></MyTable></div>
</template><script>
import MyTable from './components/MyTable.vue'
export default {data () {return {list: [{ id: 1, name: '張小花', age: 18 },{ id: 2, name: '孫大明', age: 19 },{ id: 3, name: '劉德忠', age: 17 },],list2: [{ id: 1, name: '趙小云', age: 18 },{ id: 2, name: '劉蓓蓓', age: 19 },{ id: 3, name: '姜肖泰', age: 17 },]}},methods: {del(id){this.list = this.list.filter(item=> item.id !==id)},show(row){alert(`姓名:${row.name} 年紀:${row.age}`)}},components: {MyTable}
}
</script>
18.商品列表
// my-tag 標簽組件的封裝
// 1. 創建組件 - 初始化
// 2. 實現功能
// (1) 雙擊顯示,并且自動聚焦
// v-if v-else @dbclick 操作 isEdit
// 自動聚焦:
// 1. $nextTick => $refs 獲取到dom,進行focus獲取焦點
// 2. 封裝v-focus指令// (2) 失去焦點,隱藏輸入框
// @blur 操作 isEdit 即可// (3) 回顯標簽信息
// 回顯的標簽信息是父組件傳遞過來的
// v-model實現功能 (簡化代碼) v-model => :value 和 @input
// 組件內部通過props接收, :value設置給輸入框// (4) 內容修改了,回車 => 修改標簽信息
// @keyup.enter, 觸發事件 $emit('input', e.target.value)// ---------------------------------------------------------------------
// my-table 表格組件的封裝
// 1. 數據不能寫死,動態傳遞表格渲染的數據 props
// 2. 結構不能寫死 - 多處結構自定義 【具名插槽】
// (1) 表頭支持自定義
// (2) 主體支持自定義
19.路由的基本使用
路由就是路徑和組件之間的對應關系
router-view控制組件展示的位置
<router-view></router-view>
// 路由的使用步驟 5 + 2
// 5個基礎步驟
// 1. 下載 v3.6.5
// 2. 引入
// 3. 安裝注冊 Vue.use(Vue插件)
// 4. 創建路由對象
// 5. 注入到new Vue中,建立關聯// 2個核心步驟
// 1. 建組件(views目錄),配規則
// 2. 準備導航鏈接,配置路由出口(匹配的組件展示的位置)
import Find from './views/Find'
import My from './views/My'
import Friend from './views/Friend'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化const router = new VueRouter({// routes 路由規則們// route 一條路由規則 { path: 路徑, component: 組件 }routes: [{ path: '/find', component: Find },{ path: '/my', component: My },{ path: '/friend', component: Friend },]
})Vue.config.productionTip = falsenew Vue({render: h => h(App),router
}).$mount('#app')
20.組件存放目錄問題
頁面組件,放在views里面,頁面展示,配合路由使用
復用組件,頁面里面的小復用組件,放在components里面,數據展示,用于復用
21.路由的封裝分離
import Find from '@/views/Find'
import My from '@/views/My'
import Friend from '@/views/Friend'import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化// 創建了一個路由對象
const router = new VueRouter({// routes 路由規則們// route 一條路由規則 { path: 路徑, component: 組件 }routes: [{ path: '/find', component: Find },{ path: '/my', component: My },{ path: '/friend', component: Friend },]
})//導出路由
export default router
22.用router-link代替a標簽實現高亮
<template><div><div class="footer_wrap"><router-link to="/find">發現音樂</router-link><router-link to="/my">我的音樂</router-link><router-link to="/friend">朋友</router-link></div><div class="top"><!-- 路由出口 → 匹配的組件所展示的位置 --><router-view></router-view></div></div>
</template>
router-link-active 模糊匹配(更多) to=“/find” => 地址欄 /find /find/one /find/two …
router-link-exact-active 精確匹配 to=“/find” => 地址欄 /find
23.自定義匹配的類型
// link自定義高亮類名linkActiveClass: 'active', // 配置模糊匹配的類名linkExactActiveClass: 'exact-active' // 配置精確匹配的類名
跳轉傳參
一旦說到聲明式導航就是routerlink
- 查詢參數傳參
- to=“/path?參數名=值”
子組件熱門搜索:<router-link to="/search/黑馬程序員">黑馬程序員</router-link><router-link to="/search/前端培訓">前端培訓</router-link><router-link to="/search/如何成為前端大牛">如何成為前端大牛</router-link>
- $router.query.參數名
在模板中可以省略this,但是在js中的created中要加this才行
子組件<p>搜索關鍵字: {{ $route.params.words }} </p><p>搜索結果: </p><script>
export default {name: 'MyFriend',created () {// 在created中,獲取路由參數// this.$route.query.參數名 獲取查詢參數// this.$route.params.參數名 獲取動態路由參數console.log(this.$route.params.words);}
}
</script>
- 動態路由傳參
- 配置動態路由
const router = new VueRouter({routes: [{ path: '/home', component: Home },{ path: '/search/:words?', component: Search }]
})
- 配置導航鏈接
to=“/path/參數值”
- 對應頁面組件接收傳遞過來的值
$router.params.參數名
區別:
查詢參數傳參(適合多個參數)
跳轉:to=“/path?參數名=值&參數名2=值”
獲取: r o u t e r . q u e r y . 參數名動態路由傳參(更優雅,適合單個參數傳參)配置動態路由: p a t h : ′ / s e a r c h / : w o r d s ′ 跳轉: t o = " / p a t h / 參數值 " 獲取: router.query.參數名 動態路由傳參(更優雅,適合單個參數傳參) 配置動態路由:path: '/search/:words' 跳轉:to="/path/參數值" 獲取: router.query.參數名動態路由傳參(更優雅,適合單個參數傳參)配置動態路由:path:′/search/:words′跳轉:to="/path/參數值"獲取:router.params.參數名
動態路由參數可選符
/search/:words表示必須要傳參數,如果不傳參數,也希望匹配,加個可選符 ?
24. 路由重定向
重定向:強制轉換路徑
語法:
{ path: '/', redirect: '/home' },
vue路由-404
作用:當路徑找不到匹配時,給個提示頁面
位置:配在路由最后
語法:
const router = new VueRouter({routes: [{ path: '/', redirect: '/home' },{ path: '/home', component: Home },{ path: '/search/:words?', component: Search },{ path: '*', component: NotFound }]
})
模式設置
history,以后上線需要服務器端支持
- mode: ‘history’
const router = new VueRouter({// 注意:一旦采用了 history 模式,地址欄就沒有 #,需要后臺配置訪問規則mode: 'history',routes: [{ path: '/', redirect: '/home' },{ path: '/home', component: Home },{ name: 'search', path: '/search/:words?', component: Search },{ path: '*', component: NotFound }]
})
25.編程式導航-基本跳轉
- path 路徑跳轉(簡易方便)
methods: {goSearch () {// 1. 通過路徑的方式跳轉// (1) this.$router.push('路由路徑') [簡寫]this.$router.push('/search')
或者:// (2) this.$router.push({ [完整寫法]// path: '路由路徑' // })this.$router.push({path: '/search'})}}
- 通過命名路由的方式跳轉 (需要給路由起名字) 適合長路徑
methods: {goSearch () {// 2. 通過命名路由的方式跳轉 (需要給路由起名字) 適合長路徑// this.$router.push({// name: '路由名'// })this.$router.push({name: 'search'})}}
編程式導航-路由跳轉傳參
兩種跳轉方式對于兩種傳參方式都支持
跳轉方式:
- path路徑跳轉傳參(query傳參)
- name命名路由跳轉傳參
path路徑跳轉傳參語法
this.$router.push('路由路徑?參數名=參數值')
this.$router.push(`/search?key=${this.inpValue}`)
完整寫法this.$router.push({ [完整寫法] 更適合傳參path: '路由路徑',query: {參數名: 參數值,參數名: 參數值}})
動態路由傳參
this.$router.push(`/search/${this.inpValue}`)
前提是以修改路由路徑
{ name: 'search', path: '/search/:words?', component: Search },
查詢參數:
query ?
動態路由:
params /
name命名路由跳轉傳參語法:
query傳參this.$router.push({ [完整寫法] 更適合傳參name: '路由名字',query: {參數名: 參數值,參數名: 參數值}})
動態路由傳參,params傳params接收
this.$router.push({name: 'search',params: {words: this.inpValue}})
路徑長選path,參數多選query
26.面經基礎版
組件緩存keep-alive
把原來已經加載過的數據緩存下來
keep-alive的三個屬性(組件名數組):
- include: 只有匹配的組件會被緩存
- exclude: 任何匹配的組件都不會被緩存
- max: 最多可以緩存多少組件實例
一般2與3一起,1與2不同時使用
27. 自定義創建項目
28.ESlint代碼規范
JavaScript Standard Style 規范說明:https://standardjs.com/rules-zhcn.html
規則中的一部分:
(1)字符串使用單引號 ‘abc’
(2)無分號 const name = ‘zs’
(3)關鍵字后加空格 if(name = ‘ls’) {…}
(4)函數名后加空格 function name (arg) {…}
(5)堅持使用全等 = = = 摒棄 = =
解決方案:
- 手動修正
- 自動修正