前沿
置身世外只爲暗中觀察!!!Hello大家好,我是魔王哪吒!重學鞏固你的Vuejs知識體系,如果有哪些知識點遺漏,還望在評論中說明,讓我可以及時更新本篇內容知識體系。歡迎點贊收藏!
生命週期
首先:new Vue()
,new
一個Vue
的實例,Observe data
數據查看,init Events
綁定事件,created
執行created
方法,判斷是否有el
屬性,如果沒有,vm.$mount(el)
表示處於未掛載狀態,可以手動調用這個方法來掛載。判斷是否有template
屬性。
如果有el
屬性,判斷是否有template
屬性。
實例化期和加載期
創建期間的生命週期函數:beforeCreate
和 created
,beforeMount
和 mounted
。
beforeCreate
在實例初始化後,數據觀測data observer
和event/watcher
事件配置之前被調用。
更新期
運行期間的生命週期函數:beforeUpdate
和 updated
created
實例已經創建完成後被調用。
實例已完成以下的配置:數據觀測data observer
,屬性和方法的運算,watch/event
事件回調。
掛載階段還沒開始,$el
屬性目前不可見。
beforeMount
在掛載開始之前被調用,相關的render
函數首次被調用。mounted
,vm.$el
已經掛載在文檔內,對已有dom
節點的操作可以在期間進行。beforeUpdate
數據更新時調用,發生在虛擬dmo
重新渲染和打補丁之前。updated
當這個鉤子被調用時,組件dom
已經更新,所以你現在可以執行依賴於dom
的操作。activated
,deactivated
,beforeDestroy
,destroyed
。實例銷燬之前調用,vue
實例銷燬後調用。
卸載期
銷燬期間的生命週期函數:beforeDestroy
和 destroyed
實例生命週期鉤子
每個vue實例在被創建時都要經過一系列的初始化過程,需要設置數據監聽,編譯模板,將實例掛載到dom
並在數據變化時更新dom
等,同時在這個過程中也會運行一些叫做生命週期鉤子的函數。
用於給用戶在不同階段添加自己代碼的機會。
beforeCreate
,此時的data
是不可見的
data() {
return {
a: 1
}
},
beforeCreate() {
// red
console.log(this.a); // 看不見
}
created
實例已經創建完成後被調用,這個時候你看不見你頁面的內容,實例已完成表示:數據觀測data observer
,屬性和方法的運算,watch/event
事件回調。
這個時候掛載階段還沒開始,$el
屬性目前不可見。
export default {
data() {
return {
a: 1
}
},
beforeCreate() {
console.log(this.a);
},
created() {
// red
console.log(this.a);
console.log(this.$el);
// 此時data數據裏面的a可見,this.$el不可見
}
}
beforeMount
在掛載開始之前被調用,相關的render
函數首次被調用。
export default{
data() {
return {
a: 1
}
},
beforeCreate() {
console.log(this.a); // 不可見
},
created() {
console.log(this.a);
console.log(this.$el); // 不可見
},
beforeMount() {
console.log(this.$el); // 不可見
}
}
mounted
:
export default {
data() {
return {
a: 1
}
},
mounted() {
console.log(this.$el); // 此時$el 可見
}
}
beforeUpdate
鉤子,dom
更新之前調用:
beforeUpdate() {
console.log(this.a);
}
// document.getElementById("web").innerHTML
updated
鉤子,dom
更新之後調用:
updated() {
console.log(this.a);
}
// document.getElementById("web").innerHTML
activated
和deactivated
(組件)
activated() {
console.log("組件使用了");
},
deactivated() {
console.log("組件停用了");
Data to Drag},
keep-alive
是vue
的內置組件,能在組件切換過程中將狀態保留在內存中,防止重複渲染dom
。
<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不會銷燬它們。和<transition>
相似,<keep-alive>
是一個抽象組件:它自身不會渲染一個DOM
元素,也不會出現在父組件鏈中。
當組件在<keep-alive>
內被切換,它的activated
和deactivated
這兩個生命週期鉤子函數將會被對應指定。
它的使用是因爲我們不希望組件被重新渲染而影響使用體驗,或者是性能,避免多次渲染降低性能。緩存下來,維持當前得狀態。
場景:
- 商品列表頁點擊商品跳轉到商品詳情,返回後仍顯示原有信息
- 訂單列表跳轉到訂單詳情,返回,等等場景。
keep-alive
生命週期:
初次進入時:created > mounted > activated;退出後觸發 deactivated
;再次進入:會觸發activated
;事件掛載的方法等,只執行一次的放在mounted
中;組件每次進去執行的方法放在 activated
中。
app.vue
父組件:
<template>
<div>
<button @click="myBtn"> myBtn </button>
<keep-alive>
<range v-if="isShow"></range>
</keep-alive>
</div>
</template>
<script>
import range from './components/range.vue'
export default {
data() {
return {
a: 1,
isShow: true
}
},
methods: {
myBtn() {
this.isShow = !this.isShow
}
},
components: {
range
}
}
</script>
beforeDestroy
和destroyed
beeforeDestroy
類型爲function
,詳細:實例銷燬之前調用,在這一步,實例仍然完全可用。
該鉤子在服務器端渲染期間不被調用。
destroyed
類型爲function
,詳細:vue
實例銷燬後調用,調用後,vue
實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷燬。
該鉤子在服務器端渲染期間不被調用。
beforeRouteEnter
和beforeRouteLeave
beforeRouteEnter() {
console.log('beforeRouteEnter')
},
beforeRouteLeave() {
console.log('beforeRouteLeave')
}
vue路由使用的,路由進去和路由離開的時候添加的。
created() {
console.log('開始執行created鉤子函數')
// 獲取data數據
console.log('獲取created屬性'+this.value)
// 獲取頁面元素
console.log(this.$refs['example'])
this.$nextTick(()=>{
console.log('執行created創建的this.$nextTick()函數')
})
},
mounted() {
console.log('開始執行mounted鉤子函數')
// 獲取掛載數據
console.log('獲取掛載數據--'+this.$refs['example'].innerText)
this.$nextTick(()=>{
console.log('執行mounted創建的this.$nextTick()函數')
})
},
methods: {
// 更新數據
updateDate(){
},
get(){
this.value='更新data內的value屬性值'
// 獲取頁面元素數據
console.log(this.$refs['example').innerText)
this.$nextTick(()=>{
console.log(this.$refs['example'].innerText)
})
}
}
var vm=new Vue({})
表示開始創建一個Vue
的實例對象,init events&liftcycle
表示剛初始化了一個vue
空的實例對象,這個時候,對象身上,只有默認的一些生命週期函數和默認事件,其他東西都沒有創建,beforeCreate
生命週期函數執行的時候,data
和methods
中的數據都沒有初始化。在created
中,data
和methods
都已經被初始化好了,如果要調用methods
中的方法,或者操作data
中的數據,只能在created
中操作。然後vue
開始編輯模板,把vue
代碼中的那些指令進行執行,最終在內存中生成一個編譯好的最終模板字符串,渲染爲內存中的dom
,此時只是在內存中,渲染好了模板,並沒有把模板掛載到真正的頁面中去。beforeMount
函數執行的時候,模板已經在內存中編譯好了,但是尚未掛載到頁面中去。create vm.$el and replace 'el' with it
這一步是將內存中編譯好的模板,真實的替換到瀏覽器的頁面中去。mounted
,只要執行完了mounted
,就表示整個vue
實例已經初始化完了。此時,組件從創建階段進入到了運行階段。
beforeUpdate
執行的時候,頁面中顯示的數據還舊的,而data
數據是最新的,頁面尚未和最新的數據保持同步。updated
事件執行的時候,頁面和data
數據已經保持同步了,都是新的。virtual dom re-render and patch
執行,先根據data
中最新的數據,在內存中,重新渲染出一份最新的內存dom
樹,當最新的內存dom
樹被更新之後,會把最新的內存dom
樹,重新渲染到真實的頁面中,完成數據從data
到view
的跟新。
beforeDestroy
鉤子函數執行時,vue
實例就從運行階段,進入到了銷燬階段。此時的實例還是可用的階段,沒有真正執行銷燬過程。destroyed
函數執行時,組件已經被完全銷燬了,都不可用了。
vue面試題
談一談你對mvvm
的理解
雙向綁定的過程
視圖view
,路由-控制器Controller
,數據Model
view
->dom
,viewModel
,Model
數據
傳統的mvc
指用戶操作會請求服務器端路由,路由會調用對應的控制器來處理,控制器會獲取數據,將結果返回給前端,讓頁面重新渲染。
mvvm
,對於傳統的前端會將數據手動渲染到頁面上,mvvm
模式不需要用戶收到操作dom
元素,將數據綁定到viewModel
層上,會自動將數據渲染到頁面中,視圖變化會通知viewModel
層更新數據。
Vue響應式原理
-
vue
內部是如何監聽message
數據的改變 - 當數據發生改變,
vue
是如何知道要通知哪些人,界面發生刷新
核心:
-
Object.defineProperty
,監聽對象屬性的改變 - 發佈訂閱者模式
代碼:
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
set(newValue) {
// 監聽改變
value = newValue
},
get() {
return value
}
})
})
obj.name = 'web'
發佈者訂閱者
class Dep {
constructor() {
this.subs = []
}
}
class Watcher {
constructor(name) {
this.name = name;
}
}
對象的Object.defindeProperty
中的訪問器屬性中的get
和set
方法
- 把數據轉化爲
getter
和setter
,建立watcher
並收集依賴。
說明:
watcher
通過回調函數更新view
;observer
觀測data
數據,通過get
通知dep
收集watcher
,dep
通過notify()
通知watcher
數據更新,watcher
通過addDep()
收集依賴。
Observer
:用於監聽劫持所有data
屬性,dep,watcher,view
,Compile
解析el
模板中的指令。
首先從初始化data
數據開始,使用Observer
監聽數據,個體每個數據屬性添加Dep
,並且在Data
,有兩個getter
,setter
。在它的getter
過程添加收集依賴操作,在setter
過程添加通知依賴的操作。
在解析指令或者給vue
實例設置watch選項或者調用$watch
時,生成對應的watcher
並收集依賴。
Data
通過Observer
轉換成了getter/setter
的形式,來對數據追蹤變化。
修改對象的值的時候,會觸發對應的setter
,setter
通知之前依賴收集得到的 Dep
中的每一個Watcher
,告訴它們值改變了,需要重新渲染視圖。
[圖片上傳失敗...(image-7de041-1603506651661)]
數據雙向綁定原理
什麼是響應式的原理
- 核心:
Object.defineProperty
- 默認
vue
在初始化數據時,會給data
中的屬性使用Object.defineProperty
重新定義所有屬性,當頁面取到對應屬性時,會進行依賴收集,如果屬性發生變化會通知相關依賴進行更新操作。
initData
初始化用戶傳入的data
數據,new Observer
將數據進行觀測,this.walk(value)
進行對象的處理,defineReactive
循環對象屬性定義響應式變化,Object.defineProperty
,使用Object.defineProperty
重新定義數據。
使用使用Object.defineProperty
重新定義數據的每一項。
Object.defineProperty(obj,key,{
enumerable: true,
configurable: true,
get: function reactiveGetter(){
const value=getter?getter.call(obj):val
if(Dep.target){
dep.depend()
if(childOb){
childOb.dep.depend()
if(Array.isArray(value)){
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter(newVal) {
const value=getter?getter.call(obj).val
if(newVal === value || (newVal !== newVal && value !==value)){
return
}
if(process.env.NODE_ENV !== 'production' && customSetter){
customSetter()
}
val = newVal
childOb = !shallow && observe(newVal)
dep.notify()
}
})
vue中式如何檢測數組變化
使用函數劫持的方式,重寫了數組的方法,vue
將data
中的數組進行了原型鏈的重寫,指向了自己定義的數組原型方法,這樣當調用數組api
時,可以通知依賴跟新,如果數組中包含着引用類型,會對數組中的引用類型再次進行監控。
initData
初始化用戶傳入的data
數據,new Observer
將數據進行觀測,protoAugment(value,arrayMethods)
將數據的原型方法指向重寫的原型。
- 對數組的原型方法進行重寫
-
observerArray
深度觀察數組中的每一項
代碼:
if(Array.isArray(value)){
// 判斷數組
if(hasProto){
protoAugment(value, arrayMethods)// 改寫數組原型方法
}else{
copyAugment(value,arrayMethods,arrayKeys)
}
this.observeArray(value)
//深度觀察數組中的每一項
}else{
this.walk(value)
// 重新定義對象類型數據
}
function protoAugment(target, src: Object){
target.__proto__ = src
}
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch=[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method){
const original = arrayProto[method]
def(arrayMethods, method, function mutator(...args){
const result = original.apply(this.args)
const ob = this.__ob__
let inserted
switch(method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if(inserted) ob.observerArray(inserted)
// 對插入的數據再次進行觀測
ob.dep.notify()
// 通知視圖更新
return result
}
}
observeArray(items: Array<any>) {
for(let i=0, l = items.length; i<1; i++) {
observe(item[i])
// 觀測數組中的每一項
}
}
爲什麼vue採用異步渲染
如果不採用異步更新,每次更新數據都會對當前組件進行重新渲染,爲了性能考慮。
dep.notify()
通知watcher
進行更新操作,subs[i].update()
依次調用watcher
的update
,queueWatcher
將watcher
去重放到隊列中,nextTick(flushSchedulerQueue)
異步清空watcher
隊列。
nextTick實現原理
微任務高於宏任務先執行
nextTick
方法主要使用了宏任務和微任務,定義了一個異步方法,多次調用了nextTick
會將方法存入到隊列中,通過這個異步方法清空當前隊列。
nextTick
方法是異步方法。
原理:nextTick(cb)
調用nextTick
傳入cb
,callbacks.push(cb)
將回調存入數組中,timerFunc()
調用timerFunc
,返回promise
支持promise
的寫法。
webpack
什麼是webpack,webpack是一個現代的JavaScript應用的靜態模塊打包工具。
[圖片上傳失敗...(image-94a2fa-1603506651661)]
webpack是前端模塊化打包工具
安裝webpack需要安裝node.js,node.js自帶有軟件包管理工具npm
[圖片上傳失敗...(image-eb8fc0-1603506651661)]
全局安裝
npm install [email protected] -g
局部安裝
npm install [email protected] --save-dev
webpack.config.js
固定名文件:
const path = require("path")
module.exports = {
entry: './src/main.js',
output: {
patch: './dist',
filename: ''
},
}
package.json
{
"name": 'meetwebpack',
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo ..."
},
"author": "",
"license": "ISC"
}
什麼是loader
loader
是webpack
中一個非常核心的概念
loader
使用過程:
- 通過
npm
安裝需要使用的loader
- 在
webpack.config.js
中的moudules
關鍵字下進行配置
package.json
中定義啓動
{
"name": "meetwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^3.6.0"
}
}
webpack
的介紹
webpack
可以看做是模塊打包機,它可以分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言,將其打包爲合適的格式以供瀏覽器使用。
可以實現代碼的轉換,文件優化,代碼分割,模塊合併,自動刷新,代碼校驗,自動發佈。
安裝本地的webpack
webpack webpack-cli -D
初始化:
yarn init -y
yarn add webpack webpack-cli -D
webpack可以進行0配置,它是一個打包工具,可以輸出後的結果(Js模塊),打包(支持js的模塊化)
運行webpack命令打包
npx webpack
webpack.config.js
,webpack
是node
寫出來的node
的寫法:
let path = require('path')
console.log(path.resolve('dist');
module.exports = {
mode: 'development',
// 模式,默認兩種,production,development
entry: '' // 入口
output: {
filename: 'bundle.js',
// 打包後的文件名
path: path.resolve(__dirname, 'build'),
// 把相對路徑改寫爲絕對路徑
}
}
自定義,webpack.config.my.js
使用命令:
npx webpack --config webpack.config.my.js
package.json
:
{
"name": 'webpack-dev-1',
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack --config webpack.config.my.js"
},
"devDependencies": {
"webpack": "^4.28.3",
"webpack-cli": "^3.2.0"
}
}
使用命令:
npm run build
// npm run build -- --config webpack.config.my.js
開發服務器的配置
代碼:
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin')
console.log(path.resolve('dist');
module.exports = {
devServer: {
// 開發服務器的配置
port: 3000,
// 看到進度條
progress: true,
contentBase: "./build",
compress: true
},
mode: 'development',
// 模式,默認兩種,production,development
entry: '' // 入口
output: {
filename: 'bundle.js',
// 打包後的文件名
path: path.resolve(__dirname, 'build'),
// 把相對路徑改寫爲絕對路徑
},
plugins: [
// 數組,所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify:{
removeAttributeQuotes: true,//刪除“”
collapseWhitespace: true, // 變成一行
},
hash: true
})
],
module: {
// 模塊
rules: [
// 規則
{test: /\.css$/, use: [{
loader: 'style-loader',
options: {
insertAt: 'top'
}
},'css-loader'] },
]
}
}
output: {
filename: 'bundle.[hash:8].js',// 打包文件名後只顯示8位
}
{
"name": 'webpack-dev-1',
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack --config webpack.config.my.js",
"dev": "webpack-dev-server"
},
"devDependencies": {
"webpack": "^4.28.3",
"webpack-cli": "^3.2.0"
}
}
yarn add css-loader style-loader -D
樣式:
-
style-loader
將模塊的導出作爲樣式添加到dom
中 -
css-loader
解析css
文件後,使用import
加載,並且返回css
代碼 -
less-loader
加載和轉譯less
文件 -
sass-loader
加載和轉譯sass/scss
文件 -
postcss-loader
使用PostCSS
加載和轉譯css/sss
文件 -
stylus-loader
加載和轉譯Stylus
文件
style-loader
安裝:
npm install style-loader --save-dev
用法:
建議將style-loader
與css-loader
結合使用
component.js
import style from './file.css'
-
css-loader
只負責將css文件進行加載 -
style-loader
負責將樣式添加到dom
中 - 使用多個
loader
時,是從右到左
代碼:
// webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
css
文件處理:style-loader
安裝style-loader
npm install --save-dev style-loader
style-loader
需要放在css-loader
的前面,webpack
在讀取使用的loader
的過程中,是按照從右向左的順序讀取的。
webpack.config.js
的配置如下:
const path = require('path')
module.exports = {
// 入口:可以是字符串/數組/對象,這裏我們入口只有一個,所以寫一個字符串即可。
entry: './src/main.js',
// 出口:通常是一個對象,裏面至少包含兩個重要屬性,path和filename
output:{
path: path.resolve(__dirname, 'dist'), // 注意:path通常是一個絕對路徑
filename: 'bundle.js'
},
module: {
rules: {
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
}
}
}
webpack
less文件處理
安裝:
npm install --save-dev less-loader less
示例:
將css-loader
,style-loader
,less-loader
鏈式調用,可以把所有樣式立即應用於dom
。
// webpack.config.js
module.exports = {
...
rules: [{
test: /\.less$/,
use: [{
loader: 'style-loader'
},{
loader: 'css-loader'
},{
loader: 'less-loader'
}]
}]
}
圖片文件處理
css normal
代碼:
body {
background: url("../img/test.jpg")
}
url-loader
npm install --save-dev url-loader
用法
url-loader
功能類似於file-loader
,但是在文件大小低於指定的限制時,可以返回一個DataURL
import img from './image.png'
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
]
}
}
img
,文件要打包到的文件夾
name
,獲取圖片原來的名字,放在該位置
hash:8
,爲了防止圖片名稱衝突,依然使用hash
,但是我們只保留8位
ext
,使用圖片原來的擴展名
es6轉es5的babel
如果希望es6轉成es5,那麼就需要使用babel
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
配置webpack.config.js
文件:
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
使用vue
如何在我們的webpack
環境中集成vue.js
代碼:
npm install vue --save
-
runtime-only
代碼中,不可以有任何的template
-
runtime-compiler
代碼中,可以有template
因爲有compiler
可以用於編譯template
spa
(simple age web application
)->vue-router
(前端路由)
.vue
文件封裝處理
安裝vue-loader
和vue-template-compiler
npm install vue-loader vue-template-compiler --save-dev
認識webpack的plugin
-
plugin
是什麼?
-
plugin
是插件的意思,通常用於對某個現有的架構進行擴展。 -
webpack
中的插件,是對webpack
現有功能的各種擴展。
-
loader
和plugin
的區別
-
loader
主要用於轉換某些類型的模塊,它是一個轉換器。 -
plugin
是插件,它是對webpack
本身的擴展,是一個擴展器。
-
plugin
的使用過程:
- 通過
npm
安裝需要使用的plugins
- 在
webpack.config.js
中的plugins
中配置插件
webpack.config.js
的文件:
查看bundle.js
文件的頭部:
Vue Cli詳解
什麼是vue cli
,Command-Line Interface
,命令行界面,俗稱腳手架,vue cli
是一個官方發佈的項目腳手架。使用vue-cli
可以快速搭建vue
開發環境以及對應的webpack
配置。
vue cli
的使用
安裝vue
腳手架
npm install -g @vue/cli
vuecli2初始化過程
代碼:
vue init webpack vuecli2test
- 根據名稱創建一個文件夾,存放之後項目的內容,該名稱會作爲默認的項目名稱,但是不能包含大寫字母等
-
Project name
項目名稱,不能包含大寫 -
Project description
項目描述 -
Author
作者信息 Vue build``runtime
-
Install vue-router``no
是否安裝等
目錄結構詳解
build``config
是 webpack
相關配置,node_modules
是依賴的node
相關的模塊,src
是寫代碼地方。 .babelrc
是es代碼相關轉換配置,.editorconfig
項目文本相關配置,.gitignore``git
倉庫忽略的文件夾配置,.postcssrc.js
爲css
相關轉化的配置。
.editorconfig
前端模塊化:
爲什麼使用模塊化,簡單寫js代碼帶來的問題,閉包引起代碼不可複用,自己實現了簡單的模塊化,es
中模塊化的使用:export
和import
。
npm install @vue/cli -g
npm clean cache -force
vue cli2
初始化:
vue init webpack my-project
vue cli3
初始化項目:
vue create my-project
箭頭函數的使用和this
箭頭函數,是一種定義函數的方式
- 定義函數的方式:
function
const a = function(){
}
- 對象字面量中定義函數
const obj = {
b: function() {
},
b() {
}
}
- 箭頭函數
const c = (參數列表) => {
}
const c = () => {
}
箭頭函數參數和返回值
代碼:
const sum = (num1, num2) => {
return num1 + num2
}
const power = (num) => {
return num * num
}
const num = (num1,num2) => num1 + num2
const obj = {
a() {
setTimeout(function() {
console.log(this); // window
})
setTimeout(()=>{
console.log(this); // obj對象
})
}
}
路由,,vue-router
基本使用,vue-router
嵌套路由,vue-router
參數傳遞,vue-router
導航守衛。
路由是一個網絡工程裏面的術語,路由就是通過互聯的網絡把信息從源地址傳輸到目的地址的活動。
路由器提供了兩種機制:路由和轉送。路由是決定數據包從來源到目的地的路徑,轉送將輸入端的數據轉移到合適的輸出端。路由中有一個非常重要的概念叫路由表。路由表本質上就是一個映射表,決定了數據包的指向。
後端路由:後端處理url和頁面之間的映射關係。
前端路由和後端路由,前端渲染和後端渲染
vue-router
和koa-router
的區別:
vue-router
是前端路由,koa-router
是後端路由。
vue-router
前端路由原理:
前端路由主要模式:hash
模式和history
模式。
路由的概念來源於服務端,在服務端中路由描述的是 URL 與處理函數之間的映射關係。
前後端渲染之爭
url中的hash
和html5
的history
前端路由的核心是改變url
,但是頁面不進行整體的刷新。單頁面,其實spa
最重要的特點就是在前後端分離的基礎上加了一層前端路由。就是前端來維護一套路由規則。
url
的hash
url
的hash
是錨點#
,本質上是改變window.location
的href
屬性。直接賦值location.hash
來改變href
,但是頁面不發生刷新。
html5
的history
模式:pushState
html5
的history
模式:replaceState
html5
的history
模式:go
history.go()
history.back()
等價於history.go(-1)
history.forward()
等價於history.go(1)
安裝vue-router
npm install vue-router --save
- 導入路由對象,並且調用
Vue.use(VueRouter)
- 創建路由實例,並且傳入路由映射配置
- 在
Vue
實例中掛載創建的路由實例
代碼:
// 配置路由相關的信息
import VueRouter from 'vue-router'
import vue from 'vue'
import Home from '../components/Home'
import About from '../components/About'
// 通過Vue.use(插件),安裝插件
Vue.use(VueRouter)
// 配置路由和組件之間的應用關係
const routes = [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
// 創建VueRouter對象
const router = new VueRouter({
routes
})
// 將router對象傳入到`Vue`實例
export default router
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
使用vue-router
的步驟
- 創建路由組件
- 配置路由映射:組件和路徑映射關係
- 使用路由:通過
<router-link>
和<router-view>
代碼:
組件components
// home
<template>
<div>
<h2>我是首頁</h2>
</div>
</template>
<script>
export default {
name: 'Home'
}
</script>
<style scoped>
</style>
<template>
<div>
<h2>我是關於</h2>
</div>
</template>
<script>
export default {
name: 'Aboout'
}
</script>
<style scoped>
</style>
App.vue
<template>
<div id="app">
<router-link to="/home">首頁</router-link>
<router-link to="/about">關於</router-link>
<router-view></router-view>
</div>
</div>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
路由的偶然值和修改爲
history
模式
創建router
實例
代碼:
router->index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// 注入插件
Vue.use(VueRouter)
// 定義路由
const routes = []
// 創建router實例
const router = new VueRouter({
routes
})
// 導出router實例
export default router
main.js
代碼:
import Vue from 'vue'
import App from './App'
import router from './router'
new Vue({
el: '#app',
router,
render: h=>h(App)
})
[圖片上傳失敗...(image-7c7f54-1603506651661)]
router->index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/home'
import About from '../components/about'
// 注入插件
Vue.use(VueRouter)
// 定義路由
const routes = [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
使用App.vue
代碼
<template>
<div id="app">
<router-link to="/home">首頁</router-link>
<router-link to="/about">關於</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
-
<router-link>
該標籤是一個vue-router
已經內置的組件,它會被渲染成一個<a>
標籤 -
<router-view>
該標籤會根據當前的路徑,動態渲染出不同的組件。 - 網頁的其他內容,比如頂部的標題或導航,或者底部的一些版本信息等會和
<router-view>
處於同一個等級。 - 在路由切換時,切換的是
<router-view>
掛載的組件,其他內容不會發生改變。
路由的默認路徑
默認情況下,進入網站的首頁,<router-view>
渲染首頁的內容,但是默認沒有顯示首頁組件,必須讓用戶點擊纔可以。
那麼如何讓路徑默認跳轉到首頁,並且<router-view>
渲染首頁組件呢,只需要配置一個映射就可以:
const routes = [
{
path: '/',
redirect: '/home'
}
]
配置解析:在routes
中又配置了一個映射,path
配置的是根路徑:/
,redirect
是重定向,就是我們將根路徑重定向到/home
的路徑下。
// main.js
const router = new VueRouter({
// 配置路由和組件之間的應用關係
routes,
mode: 'history'
})
改變路徑的方式:
-
url
的hash
-
html5
的history
- 默認情況下,路徑的改變使用的
url
的hash
使用html5
的history
模式:
// 創建router實例
const router = new VueRouter({
routes,
mode: 'history'
})
router-link
,使用了一個屬性:to
,用於指定跳轉的路徑。tag
可以指定<router-link>
之後渲染成什麼組件。
replace
屬性不會留下history
記錄,指定replace
的情況下,後退鍵返回不能返回到上一個頁面中。
active-class
屬性,當<router-link>
對應的路由匹配成功時,會自動給當前元素設置一個router-link-active
的class
,設置active-class
可以修改默認的名稱。
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active'
})
路由代碼跳轉
App.vue
代碼:
// app.vue
<template>
<div id="app">
<button @click="linkToHome">首頁</button>
<button @click="linkToAbout">關於</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
linkToHome() {
this.$router.push('/home')
},
linkToAbout() {
this.$router.push('/about')
}
}
}
</script>
<img :src="imgURL" alt="">
<router-link :to="'/uer/' + userId"> 用戶 </router-link>
<script>
export default {
name: 'User',
computed: {
userId() {
return this.$route.params.userId
}
}
}
</sript>
[圖片上傳失敗...(image-db3752-1603506651661)]
const Home = () => import('../components/Home')
const HomeNews = () => import('../components/HomeNews')
const HomeMessage = () => import('../components/HomeMessage')
{
path: '/home',
component: Home,
children: [
{
path: 'news',
component: HomeNews
},
{
path: 'news',
component: HomeMessage
}
]
}
<router-link to = "/home/news">新聞</router-link>
<router-link to = "/home/message">信息</router-link>
默認選中:
傳遞參數的方式
傳遞參數主要有兩種類型,params
和query
params
的類型:
- 配置路由方式:
/router/:id
- 傳遞的方式:在
path
後面跟着對應的值 - 傳遞後形成的路徑:
/router/123
vue-router
傳遞參數代碼
<router-link :to="{path: '/profile'}">用戶</router-link>
統一資源定位符
統一資源定位符,統一資源定位器,統一資源定位地址,Url地址等,網頁地址。如同在網絡上的門牌,是因特網上標準的資源的地址。
userClick() {
this.$router.push('/user/' + this.userId)
}
btnClick() {
this.$router.push({
path: '/user',
query: {
name: 'web',
age: 12,
height: 1.2
}
})
}
$route
和$router
是有區別的
獲取參數通過$route
對象獲取的,在使用vue-router
的應用中,路由對象會被注入每個組件中,賦值爲this.$route
,並且當路由切換時,路由對象會被更新。
<template>
<div>
<p> {{$route.params}} </p>
</div>
</template>
query
的類型:
- 配置路由格式:
/router
也是普通配置 - 傳遞方式,對象中使用
query
的key
作爲傳遞方式 - 傳遞後形成的路徑,
router?id=123
,/router?id=abc
$route
和$router
是有區別的
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active'
})
Vue.config.productionTip = false
Vue.prototype.test = function() {
console.log('test')
}
Vue.prototype.name = 'web'
$route
和$router
是有區別的
-
$router
爲VueRouter
實例,想要導航到不同url
,則使用$router.push
方法。 -
$route
爲當前router
跳轉對象裏面可以獲取name
,path
,query
,params
等。
vue-router
全局導航
meta:元數據
router.beforeEach((to,from,next) => {
// from 跳轉到to
document.title = to.matched[0].meta.title
console.log(to);
next()
})
// 後置鉤子hook
router.afterEach((to,from) => {
console.log();
})
導航守衛:導航表示路由正在發生改變。
vue-router
提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中,全局的,單個路由獨享的,或者組件級的。
全局守衛
可以使用router.beforeEach
,註冊一個全局前置守衛:
const router = new VueRouter({..})
router.beforeEach((to,from,nex)=>{
})
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛resolve
完之前一直處於等待中。
-
to:Route
,即將要進入的目標路由對象 -
from:Route
,當前導航正要離開的路由 -
next:Function
,一定要調用該方法來resolve
這個鉤子。
vue-router-keep-alive
keep-alive
和vue-router
router-view
是一個組件,如果直接被包含在keep-alive
裏面,所有路徑匹配到的視圖組件都會被緩存。
keep-alive
是Vue
內置的一個組件,可以使被包含的組件保留狀態,或避免重新渲染。
屬性:
-
include
字符串或正則表達式,只有匹配的組件會被緩存 -
exclude
字符串或正則表達式,任何匹配的組件都不會被緩存
<keep-alive>
<router-view>
// 所有路徑匹配到的視圖組件都會被緩存
</router-view>
<keep-alive>
Promise
的使用
es6
的特性Promise
,它是異步編程的一種解決方案。
定時器的異步事件:
setTimeout(function() {
let data = 'web'
console.log(content)
},1000)
new Promise((resolve, reject) => {
setTimeout(function(){
resolve('web')
reject('error')
},1000)
}).then(data=>{
console.log(data)
}).catch(error=> {
console.log(error)
})
Promise
三種狀態:
-
pending
等待狀態,比如正在進行網絡請求,或定時器沒有到時間。 -
fulfill
,滿足狀態,主動回調resolve
時,並且回調.then()
-
reject
,拒絕狀態,回調reject
時,並且回調.catch()
Vuex詳解
vuex
是一個專門爲vue.js
應用程序開發的狀態管理模式
它採用集中式存儲管理應用的所有組件的狀態,,並以相應的規則保證狀態以一種可預測的方式發生變化。
- 狀態管理模式
- 集中式存儲管理
View components -> actions(dispatch方式) -> mutations(commit方式) -> state -> View components
Vuex
核心概念5個:
State
,Getters
,Mutation
,Action
,Module
State
單一狀態樹,單一數據源。
Mutation
狀態更新
Vuex
的store
的更新唯一方式,提交Mutation
Mutation
的主要包括兩部分:
- 字符串的事件類型
- 一個回調函數,該回調函數的第一個參數就是
state
mutation
的定義:
mutations: {
increment(state) {
state.count++
}
}
通過mutation
更新
increment: function() {
this.$store.commit('increment')
}
[圖片上傳失敗...(image-c248a0-1603506651661)]
參數被稱爲是mutation
的載荷payload
Vuex
的store
中的state
是響應式的,當state
中的數據發生改變時,Vue
組件會自動更新。
- 提前在
store
中初始化好所需的屬性 - 給
state
中的對象添加新屬性時:使用
- 使用
Vue.set(obj,'newObj',123)
- 用新對象給舊對象賦值
Mutation
常量類型
// mutation-types.js
export const UPDATE_INFO = 'UPDATE_INFO'
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-types'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
info: {
name: 'web',
age: 12
}
},
mutations: {
[types.UPDATE_INFO](state, payload) {
state.info = {...state.info, 'height': payload.height
}
}
})
<script>
import {UPDATE_INFO} from './store/mutation-types';
export default{
name: 'App',
components: {
},
computed: {
info(){
return this.$store.state.info
}
},
methods: {
updateInfo(){
this.$store.commit(UPDATE_INFO,{height:1.00})
}
}
}
</script>
注意:不要再mutation
中進行異步操作,mutation
同步函數,在其中的方法必須時同步方法。
action
的基本定義,如果有異步操作,比如網絡請求,
// 不能再mutation中使用異步操作,不能再這裏進行異步操作
update(state) {
setTimeout(()=>{
state.info.name = 'web'
},1000)
}
mutations: {
// 方法
[INCREMENT](state){
state.counter++
}
}
actions: {
// context:上下文,=》store
<!--aUpdateInfo(context) {-->
<!-- setTimeout(()=>{-->
<!-- state.info.name = 'web'-->
<!-- },1000)-->
<!--}-->
}
actions: {
aUpdateInfo(context) {
setTimeout(()=>{
context.commit('updateInfo')
},1000)
}
}
// xx.vue
updateInfo(){
this.$store.dispatch('aUpdateInfo')
}
updateInfo(){
<!--this.$store.commit('updateInfo')-->
this.$store.dispatch('aUpdateInfo',{
message: 'web',
success: () => {
console.log('web')
}
})
}
aUpdateInfo(context, payload) {
return new Promise((resolve, reject) => {...})
}
vuex中的modules使用
modules時模塊的意思
getters: {
stu(){
},
stuLength(state, getters) {
return getters.stu.length
}
}
使用根數據:
getters: {
fullName(state) {
return state.name + '1'
},
fullName1(state, getters) {
return getters.fullName + '2'
},
fullName3(state, getters, rootState) {
return getters.fullName2+rootState.counter
}
}
在模塊中actions
打印console.log(context)
actions
接收一個context
參數對象,局部狀態通過context.state
暴露出來,根節點狀態爲context.rootState
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const state = {
}
const store = new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
a: moduleA
}
})
export default store
網絡封裝
axios網絡模塊的封裝
ajax
是基於XMLHttpRequest(XHR)
;jQuery-Ajax
相對於傳統的ajax
非常好用。
axios
特點:
- 在瀏覽器中發送
XMLHttpRequests
請求 - 在
node.js
中發送http
請求 - 支持
Promise API
- 攔截請求和響應
- 轉換請求和響應數據
axios
請求方式:
axios(config)
axios.request(config)
axios.get()
axios.delete()
axios.head()
axios.post()
axios.put()
axios.patch()
安裝
npm install axios --save
axios({
// 默認get
url: '',
method: 'get'
}).then(res=>{
console.log(res)
})
// import request from "../utils/request.js"
import {request} from './network'
export function getHome() {
return request({
url: '/home/xxx'
})
}
export function getXX(type, page) {
return request({
url: '/home/xx',
params: {
type,
page
}
})
}
併發請求
代碼:
axios.all([axios({
url: ''
}), axios({
url: '',
params: {
type: '',
page: 1,
}
})]).then(results => {
})
// then(axios.spread((res1,res2)=>{...}))
全局配置
axios.defaults.baseURL=''
axios.all ..{
url: '/home'
}
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.baseURL = global.HOST;
request.js
import axios from 'axios'
export function request(config,success,failure){
// 創建axios實例
const instance = axios.create({
baseURL: '',
timeout: 5000
})
// 發送網絡請求
instance(config)
.then(res=>{
success(res)
})
.catch(err=>{
failure(err)
})
}
main.js
import {request} from './xx/request'
request({
url: ''
},res=>{
),err=>{
}
也可以使用promise
方法,不過本身返回的就是promise
import axios from 'axios'
export function request(config) {
const instance = axios.create({
baseURL: '',
timeout: 2000
})
return instance(config)
}
axios
攔截器的使用
// 配置請求和響應攔截
instance.interceptors.request.use(config => {
console.log('request攔截success中')
return config
},err => {
console.log('request攔截failure中')
return err
})
instance.interceptors.response.use(response => {
console.log('response攔截success中')
return response.data
},err => {
console.log('response攔截failure中')
return err
})
封裝axios
// request.js
import axios from 'axios'
cosnt service = axios.create({
baseURL: process.env.BASE_API,
timeout: 2000
})
service.interceptors.request.use(config=>{
//發請求前做的一些處理,數據轉化,配置請求頭,設置token,設置loading等
config.data=JSON.stringify(config.data);
config.headers = {
'Content-Type':'application/x-www-form-urlencoded'
}
return config
},error=>{
Promise.reject(error)
})
// 響應攔截器
service.interceptors.response.use(response => {
return response
}, error => {
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = '錯誤請求'
break;
case 401:
error.message = '未授權,請重新登錄'
break;
case 403:
error.message = '拒絕訪問'
break;
case 404:
error.message = '請求錯誤,未找到該資源'
window.location.href = "/NotFound"
break;
case 405:
error.message = '請求方法未允許'
break;
case 408:
error.message = '請求超時'
break;
case 500:
error.message = '服務器端出錯'
break;
case 501:
error.message = '網絡未實現'
break;
case 502:
error.message = '網絡錯誤'
break;
case 503:
error.message = '服務不可用'
break;
case 504:
error.message = '網絡超時'
break;
case 505:
error.message = 'http版本不支持該請求'
break;
default:
error.message = `連接錯誤${error.response.status}`
}
} else {
if (JSON.stringify(error).includes('timeout')) {
Message.error('服務器響應超時,請刷新當前頁')
}
error.message('連接服務器失敗')
}
Message.error(err.message)
return Promise.resolve(error.response)
})
// 導入文件
export default service
封裝請求http.js
import request from './request'
const http ={
/**
* methods: 請求
* @param url 請求地址
* @param params 請求參數
*/
get(url,params){
const config = {
methods: 'get',
url:url
}
if(params){
config.params = params
}
return request(config)
},
post(url,params){
const config = {
methods: 'post',
url:url
}
if(params){
config.data = params
}
return request(config)
},
put(url,params){
const config = {
methods: 'put',
url:url
}
if(params){
config.params = params
}
return request(config)
},
delete(url,params){
const config = {
methods: 'delete',
url:url
}
if(params) {
config.params = params
}
return request(config)
}
}
export default http
// api.js
import http from '../utils/http'
let resquest = "/xx/request/"
// get請求
export function getListAPI(params){
return http.get(`${resquest}/getList.json`,params)
}
// js
//創建新的axios實例,
const service = axios.create({
baseURL: process.env.BASE_API,
timeout: 3 * 1000
})
項目
創建項目:
vue create webMall
npm run serve
[圖片上傳失敗...(image-badea5-1603506651661)]
// .editorconfig
root = true
[*]
charset = utf-8
indent_style=space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
項目在
window
下部署
main.js
代碼:
import store from './store'
import FastClick from 'fastclick'
import VueLazyLoad from 'vue-lazyload'
import toast from 'components/common/toast'
Vue.config.productionTip = false
// 添加事件總線對象
Vue.prototype.$bus = new Vue()
// 安裝toast插件
Vue.use(toast)
// 解決移動端300ms延遲
FastClick.attach(document.body)
// 使用懶加載的插件
Vue.use(VueLazyLoad,{
loading: require('./xx.png')
})
windows
安裝nginx
,linux
部署,centos
上安裝nginx
linux ubuntu
Ubuntu
是一個以桌面應用爲主的Linux
操作系統,其名稱來自非洲南部祖魯語或豪薩語的“ubuntu"
一詞。
操作系統:Window10 + Centos6.5(虛擬機)
yum install nginx
systemtl start nginx.service
systemctl enable nginx.service
通過Xftp將vue項目文件上傳至雲服務器
使用Xshell連接雲服務器
[圖片上傳失敗...(image-eba039-1603506651661)]
主機就是阿里雲上創建的實例的公網ip
輸入登錄名和密碼,登錄名就是購買服務器時輸入的登錄名和密碼。
運行npm run build
命令,有一個dist文件夾,這就是vue項目打包後的文件。
nginx安裝配置
在Xshell
終端輸入命令yum install nginx
,當需要確認時輸入”y“
回車。
安裝完成後,輸入service nginx start
啓動nginx
服務。
通過命令nginx -t
查看nginx
所在的安裝目錄。
在命令行輸入命令cd/etc/nginx
切換到nginx
目錄下,再輸入cat nginx.conf
可查看當前nginx
配置文件。
輸入命令 wget https://nodejs.org/dist/v10.8.0/node-v10.8.0-linux-x64.tar.xz
回車,等待安裝。
輸入命令tar xvf node-v10.8.0-linux-x64.tar.xz
回車進行解壓操作。
小結:
- 計算屬性在多次使用時,只會調用一次,因爲它是有緩存額
- 修飾符:
stop
,prevent
,.enter
,.once
,.native
等,lazy
,number
,trim
等。 - 模板的分類寫法:
script
,template
- 父子組件的通信:父傳子,
props
,子傳父,$emit
- 項目,
npm install
,npm run serve
-
webStorm
開發vue
在Plugins
安裝插件vue.js
- 在
2.6.0
版本中,Vue
爲具名插槽和作用域插槽引入了一個新的統一的語法 (即<v-slot>
指令)。它取代了slot
和slot-scope
這兩個目前已被廢棄、尚未移除,仍在文檔中的特性。 -
v-slot
用法,分爲三類:默認插槽、具名插槽以及作用域插槽。
作用域插槽,通過 slot-scope
屬性來接受子組件傳入的屬性集合
- 默認插槽
代碼:
// 子組件
<template>
<div>
<header>
<slot>默認值</slot>
</header>
</div>
</template>
任何沒有被包裹在帶有v-slot
的<template>
中的內容都會被視爲默認插槽的內容。當子組件只有默認插槽時,<v-slot>
標籤可以直接用在組件上
// 父組件
<template>
<div>
<child>
內容1
<template>內容2</template>
內容3
</child>
<child v-slot="web">
插槽<br>
插槽<br>
</child>
</div>
</template>
- 具名插槽:
v-slot
重複定義同樣的name
後只會加載最後一個定義的插槽內容
// 子組件
<template>
<div>
<main>
<slot name="main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
- 作用域插槽:
// 子組件
<template>
<div>
<footer>
<slot name="footer" :user="user" :testBtn="testBtn">
{{user.name}}
</slot>
</footer>
</div>
</template>
<script>
exportdefault {
name: 'child',
data () {
return {
user: {
title: 'web',
name: 'web'
}
};
},
methods:{
testBtn(){
alert('web');
}
}
};
</script>
Vue
如何直接調用Component
裏的方法
<template>
<div>
<b-component ref="BComponent"></b-component>
</div>
</template>
<script>
import BComponent from './BComponent'
export default {
name: 'A',
data () {
},
components: {
BComponent
},
methods: {
callACompoentFunction () {
this.$refs.BComponent.sayHi()
}
}
}
</script>
<style scoped>
</style>
<template>
<div></div>
</template>
<script>
export default {
name: 'B',
data () {
},
methods: {
sayHi () {
console.log('web!')
}
}
}
</script>
<style scoped>
</style>
最後
我是程序員哆啦A夢,藍胖子,簡書萬粉優秀創作者,掘金優秀作者、CSDN博客專家,雲+社區社區活躍作者,致力於打造一系列能夠幫助程序員提高的優質文章。網站@http://www.dadaqianduan.cn