Github來源:一個合格的中級前端工程師需要掌握的技能 | 求星星 ✨ | 給個❤️關注,❤️點贊,❤️鼓勵一下作者
大家好,我是魔王哪吒,很高興認識你~~
哪吒人生信條:如果你所學的東西 處于喜歡 才會有強大的動力支撐。
每天學習程式設計,讓你離夢想更新一步,感謝不負每一份熱愛程式設計的程式員,不論知識點多麼奇葩,和我一起,讓那一顆四處流蕩的心定下來,一直走下去,加油,
2021
加油!歡迎關注加我
vx:xiaoda0423
,歡迎點贊、收藏和評論
不要害怕做夢,但是呢,也不要光做夢,要做一個實幹家,而不是空談家,求真力行。
前言
如果這篇文章有幫助到你,給個❤️關注,❤️點贊,❤️鼓勵一下作者,接收好挑戰了嗎?文章公衆号首發,關注 程式員哆啦A夢 第一時間擷取最新的文章
如何實作路由懶加載
{ path: '/login', component: () => import('@/views/login/index'), hidden: true },
路由懶加載中的魔法注釋
通過在注釋中指定
webpackChunkName
,可以自定義這個檔案的名字。
components = () => import(/* webpackChunkName:"login"*/ "../component/Login.vue")
windows環境搭建Vue開發環境

image.png
設定nodejs prefix(全局)和cache(緩存)路徑
在nodejs安裝路徑下,建立node_global和node_cache兩個檔案夾
- 設定緩存檔案夾
npm config set cache "D:\vueProject\nodejs\node_cache"
- 設定全局子產品存放路徑
npm config set prefix "D:\vueProject\nodejs\node_global"
基于 Node.js 安裝cnpm(淘寶鏡像)
npm install -g cnpm --registry=https://registry.npm.taobao.org
設定環境變量可以使得住任意目錄下都可以使用cnpm、vue等指令,而不需要輸入全路徑
// 系統環境變量
...\nodejs\node_global
// 使用者變量
... \nodejs\node_modules
安裝Vue
cnpm install vue -g
vue-cli 腳手架
cnpm install vue-cli -g
根據模版建立新項目
vue init webpack-simple mytest
image.png
- build,最終釋出代碼的存放的位置
- config,配置目錄,包括端口号
- node_modules,這是執行npm install後産生的,裡面包含了Node.js和npm依賴的檔案以及後續安裝的第三方元件或第三方功能
- src,存放開發頁面相關的檔案
- assets,放置一些圖檔
- components,存放了元件檔案
- App.vue,是項目入口檔案
- main.js,項目的核心檔案
- router,項目的路由
- static,一般用于存放靜态資源,如圖檔,字型等
- .babelrc檔案,用來設定轉碼的規則和插件的,一般情況不需要設定
測試一下該項目是否能夠正常工作
cnpm run dev
Prop的雙向綁定
在父子元件通訊的時候,子元件都禁止直接修改父級傳過來的prop,父元件總需要在子元件身上監聽一個事件,然後由子元件去觸發它,好讓父元件來接收到payload去改變state。
自定義元件上的
v-model
指令以及
.sync
修飾符可以解決子元件修改父元件state
一個元件上的 v-model 預設會利用名為的 prop 和名為
value
的事件
input
雙向綁定的效果:
<template>
<child-component :val="val" />
</template>
<script>
export default {
data() {
return {
val: 100
}
}
}
</script>
<template>
<div>
<div>{{val}}</div>
<button @click="handleClick">click</button>
</div>
</template>
通過v-model
父元件通過v-model傳遞val值:
<template>
<child-component v-model="val" />
</template>
而子元件内通過
model
選項去綁定這個prop:
export default {
model: {
prop: 'anyKey', // 不一定非要是value
event: 'anyEventName' // 不一定非要是input
},
props: {
anyKey: {
type: Number
}
},
methods: {
handleClick() {
this.$emit('anyEventName', this.anyKey+2)
}
}
}
通過.sync修飾符
父元件通過.sync修飾符傳遞val值:
<template>
<child-component :val.sync="val" />
</template>
子元件,vue内部幫我們綁定了
update:myPropName
這樣一個事件
export default {
props: {
val: {
type: Number
}
},
methods: {
handleClick() {
this.$emit('update:val', this.val+2)
}
}
}
v-bind.sync=”{ title: doc.title }”
這種綁定字面量對象,修飾符是無法正常工作的
PropSync的用法
import { Vue, Component, PropSync } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@PropSync('name', { type: String }) syncedName!: string
}
以上代碼等同于:
export default {
props: {
name: {
type: String,
},
},
computed: {
syncedName: {
get() {
return this.name
},
set(value) {
this.$emit('update:name', value)
},
},
},
}
// 父元件
<template>
<div class="PropSync">
<h1>父元件</h1>
<h2>{{name}}</h2>
<Child :name.sync="name"></Child>
</div>
</template>
<script >
import { Vue, Component } from 'vue-property-decorator';
import Child from './Child.vue';
@Component({components: { Parent }})
export default class ParentPage extends Vue {
private name = '父元件名字';
}
</script>
// 子元件
<template>
<div class="child">
<h1>子元件:</h1>
<h2>syncedName:{{ syncedName }}</h2>
<button @click="changeName">修改name</button>
</div>
</template>
<script >
import { Component, Vue, PropSync} from 'vue-property-decorator';
@Component
export default class ChildComponent extends Vue {
@PropSync('name', { type: String }) syncedName!: string; // 用來實作元件的雙向綁定,子元件可以更改父元件穿過來的值
changeName(): void {
this.syncedName = '子元件修改過後的syncedName!'; // 雙向綁定,更改syncedName會更改父元件的name
}
}
</script>
@Model的作用
@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
@Model裝飾器允許我們在一個元件上自定義v-model,接受兩個參數:
event: string類型,表示事件名;
options: PropOptions | Constructor[] | Constructor與@Prop的第一個參數一緻;
@Component
export default class YourComponent extends Vue {
@Model('change', { type: Boolean }) readonly checked!: boolean
}
等同于以下代碼:
export default {
model: {
prop: 'checked',
event: 'change',
},
props: {
checked: {
type: Boolean,
},
},
}
ModelSync的用法
@ModelSync(propName: string, event?: string, options: (PropOptions | Constructor[] | Constructor) = {})
@ModelSync裝飾器可接受三個參數:
propName: string類型,表示類型名稱;
event: string類型,表示事件名;
options: PropOptions | Constructor[] | Constructor與@Prop的第一個參數一緻;
看下面例子:
import { Vue, Component, ModelSync } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@ModelSync('checked', 'change', { type: Boolean })
readonly checkedValue!: boolean
}
以上代碼等同于:
export default {
model: {
prop: 'checked',
event: 'change',
},
props: {
checked: {
type: Boolean,
},
},
computed: {
checkedValue: {
get() {
return this.checked
},
set(value) {
this.$emit('change', value)
},
},
},
}
@Provide
import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
const symbol = Symbol('baz')
@Component
export class MyComponent extends Vue {
@Inject() readonly foo!: string
@Inject('bar') readonly bar!: string
@Inject({ from: 'optional', default: 'default' }) readonly optional!: string
@Inject(symbol) readonly baz!: string
@Provide() foo = 'foo'
@Provide('bar') baz = 'bar'
}
等同于:
const symbol = Symbol('baz')
export const MyComponent = Vue.extend({
inject: {
foo: 'foo',
bar: 'bar',
optional: { from: 'optional', default: 'default' },
baz: symbol,
},
data() {
return {
foo: 'foo',
baz: 'bar',
}
},
provide() {
return {
foo: this.foo,
bar: this.baz,
}
},
})
@ProvideReactive和@InjectReactive
這些裝飾器是@Provide和@Inject的響應式版本。如果父元件修改了提供的值,則子元件可以捕獲此修改。
const key = Symbol()
@Component
class ParentComponent extends Vue {
@ProvideReactive() one = 'value'
@ProvideReactive(key) two = 'value'
}
@Component
class ChildComponent extends Vue {
@InjectReactive() one!: string
@InjectReactive(key) two!: string
}
@Emit(event?: string)
decorator
@Emit(event?: string)
import { Vue, Component, Emit } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
count = 0
@Emit()
addToCount(n: number) {
this.count += n
}
@Emit('reset')
resetCount() {
this.count = 0
}
@Emit()
returnValue() {
return 10
}
@Emit()
onInputChange(e) {
return e.target.value
}
@Emit()
promise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(20)
}, 0)
})
}
}
等同于
export default {
data() {
return {
count: 0,
}
},
methods: {
addToCount(n) {
this.count += n
this.$emit('add-to-count', n)
},
resetCount() {
this.count = 0
this.$emit('reset')
},
returnValue() {
this.$emit('return-value', 10)
},
onInputChange(e) {
this.$emit('on-input-change', e.target.value, e)
},
promise() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve(20)
}, 0)
})
promise.then((value) => {
this.$emit('promise', value)
})
},
},
}
@Ref(refKey?: string) decorator
import { Vue, Component, Ref } from 'vue-property-decorator'
import AnotherComponent from '@/path/to/another-component.vue'
@Component
export default class YourComponent extends Vue {
@Ref() readonly anotherComponent!: AnotherComponent
@Ref('aButton') readonly button!: HTMLButtonElement
}
等同于:
export default {
computed() {
anotherComponent: {
cache: false,
get() {
return this.$refs.anotherComponent as AnotherComponent
}
},
button: {
cache: false,
get() {
return this.$refs.aButton as HTMLButtonElement
}
}
}
}
@VModel(propsArgs?: PropOptions)
decorator
@VModel(propsArgs?: PropOptions)
import { Vue, Component, VModel } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@VModel({ type: String }) name!: string
}
等同于:
export default {
props: {
value: {
type: String,
},
},
computed: {
name: {
get() {
return this.value
},
set(value) {
this.$emit('input', value)
},
},
},
}
排除打包
vue.config.js
, 添加
externals
項
configureWebpack: {
externals: {
'vue': 'Vue',
'element-ui': 'ElementUI',
'xlsx': 'XLSX'
},
resolve: {
alias: {
'@': resolve('src')
}
},
}
引用網絡資源
- 引用CDN:
- 減少應用打包出來的包體積
- 加快靜态資源的通路
- 利用浏覽器緩存,不會變動的檔案長期緩存
vue-cli3.0 開發環境建構
vue-cli安裝
(1)若已全局安裝vue-cli (1.x 或 2.x),需先解除安裝
npm uninstall vue-cli -g
(2)全局安裝 vue-cli3.0
npm install -g @vue/cli
(3)建立項目
vue create 項目名
>(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support //支援漸進式網頁應用程式
( ) Router //路由管理器
( ) Vuex //狀态管理模式(建構一個中大型單頁應用時)
( ) CSS Pre-processors //css預處理
(*) Linter / Formatter //代碼風格、格式校驗
( ) Unit Testing // 單元測試
( ) E2E Testing // 即端對端測試
啟動項目
// 1、進入項目
cd 項目名
// 2、運作
npm run serve
單頁面
image.png
前端路由模式hash
“#”代表網頁中的一個位置,右面的字元就是代表的位置資訊。
“#”意味着它不管怎麼變化都不會影響請求URL,即它隻針對浏覽器的。
在第一個#後面出現的任何字元,都會被浏覽器解讀為位置辨別符。這意味着,這些字元都不會被發送到伺服器端。如果需要發送需要先轉碼。
“#”意味着單單改變#後的部分,浏覽器隻會滾動到相應位置,不會重新加載網頁。
改變#會改變浏覽器的通路曆史
window.location.hash這個屬性可讀可寫。讀取時,可以用來判斷網頁狀态是否改變;寫入時,則會在不重載網頁的前提下,創造一條通路曆史記錄。
路由模式history
(1)History.length (隻讀)
傳回一個整數,該整數表示會話曆史中元素的數目,包括目前加載的頁。
(2)History.state (隻讀)
傳回一個表示曆史堆棧頂部的狀态的值
(3)History.scrollRestoration
允許Web應用程式在曆史導航上顯式地設定預設滾動恢複行為。
vue element input 限制輸入數字後幾位小數點
onkeyup="if(isNaN(value)) {value = null } if(value.indexOf('.')>0) { value = value.slice(0, value.indexOf('.')+5) }"
vue實作audio進度拖拽播放及拖拽播放問題解決
經初步分析,出現這種問題的原因是audio的timeupdate方法約每秒觸發一次,js代碼在播放音頻時沒有進行處理,此方法一直在修改slider的model值,當把滑塊拖到目标位置(過程超過1s)松開時,slider拖動的實際值已經被timeupdate修改成了currentTime,是以松手後滑塊會立刻回到currentTime值的位置。
隻要在拖動滑塊不松開的過程中timeupdate方法不修改slider的model值就行了,即增加一個flag,給滑塊加上滑鼠事件mousedown、mouseup,滑鼠按下(意味着在拖動)flag為true,滑鼠松開(拖動結束)flag為false,timeupdate事件隻在flag為false時才對slider的model值進行修改就行了。
<div @mousedown="audio.isDraging = true" @mouseup="audio.isDraging = false">
<el-slider
v-model="sliderTime"
:show-tooltip="false"
class="audio-content-slider"
@change="changeCurrentTime"
/>
</div>
private audio: any = {
// 音頻目前播放時長
currentTime: 0,
// 音頻最大播放時長
maxTime: 0,
// 該字段是音頻是否處于播放狀态的屬性
playing: false,
// 是否靜音
muted: false,
speed: 1,
waiting: true,
preload: 'auto',
isDraging: false
}
timeupdateFunc(e){
if(!this.isDraging){
this.playProcess = (e.target.currentTime)/this.totalTimes*100;
this.currentTime = e.target.currentTime;
}
},
git fetch vs git pull
都是從遠端拉取代碼到本地,git fetch隻是拉取到本地,git pull不僅拉取到本地還merge到本地分支中。是以git pull是git fetch與git merge的集合體。
npm config list
$ npm config list
; "builtin" config from c:\vue\node_global\node_modules\npm\npmrc
; prefix = "C:\\Users\\da\\AppData\\Roaming\\npm" ; overridden by user
; "user" config from C:\Users\da\.npmrc
ca = [""]
cache = "C:\\vue\\node_cache"
get = "registry"
prefix = "c:\\vue\\node_global"
registry = "https://registry.npmjs.org/"
strict-ssl = true
; node bin location = C:\vue\node.exe
; cwd = C:\HBuilderProjects
; HOME = C:\Users\da
; Run `npm config ls -l` to show all defaults.
npm config set registry https://registry.npmjs.org
npm config set registry https://registry.npm.taobao.org
原因是https的自簽名失敗
臨時解決辦法:關閉ssl
npm config set strict-ssl false
$ npm get registry
https://registry.npmjs.org/
$ npm uninstall -g vue-cli
npm ERR! code EPERM
npm ERR! syscall mkdir
npm ERR! path c:\vue\node_global\node_modules\.vue-cli-SVGezKmI
npm ERR! errno -4048
npm ERR! Error: EPERM: operation not permitted, mkdir 'c:\vue\node_global\node_modules\.vue-cli-SVGezKmI'
npm ERR! [Error: EPERM: operation not permitted, mkdir 'c:\vue\node_global\node_modules\.vue-cli-SVGezKmI'] {
npm ERR! errno: -4048,
npm ERR! code: 'EPERM',
npm ERR! syscall: 'mkdir',
npm ERR! path: 'c:\\vue\\node_global\\node_modules\\.vue-cli-SVGezKmI'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It's possible that the file was already in use (by a text editor or antivirus),
npm ERR! or that you lack permissions to access it.
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.
image.png
image.png
$ node -v
v14.17.3
$ vue -V
2.9.6
$ npm -v
7.20.1
$ vue -V
@vue/cli 4.5.13
vue 建立項目有必要安裝Progressive Web App(PWA)Support嗎
簡單說一下:
一是給項目添加一些webapp支援,比如在手機端支援發送到桌面圖示,根據不同平台和浏覽器嘗試去掉浏覽器自帶的位址欄、底欄實作全屏體驗,這個主要是視覺上和體驗上的,沒有什麼實際功能。
實作方式就是勾選pwa支援後項目會生成manifest.json,在裡面配置即可
二是增加可離線支援。其實可離線也不一定非要用pwa,有不少其他手段。可離線就是比如你的項目不是一定要全程聯網才能實作功能,隻要使用者通路過一次你的網站,下一次進入時哪怕沒有網絡,你的項目也不會白屏,而是照常運作或者開放部分功能,或者給個斷網提示等等。對于那些功能性網站挺有用的,舉例來說什麼線上電腦,線上算稅小工具等。
通過配置項目生成的registerServiceWorker.js來注冊serviceworker實作,具體操作還是很複雜的,詳情百度.
PWA 的主要特點包括下面三點:
- 可靠 - 即使在不穩定的網絡環境下,也能瞬間加載并展現
- 體驗 - 快速響應,并且有平滑的動畫響應使用者的操作
- 粘性 - 像裝置上的原生應用,具有沉浸式的使用者體驗,使用者可以添加到桌面
PWA 具有下面一些特性:
- 漸進式 - 适用于所有浏覽器,因為它是以漸進式增強作為宗旨開發的
- 連接配接無關性 - 能夠借助 Service Worker 在離線或者網絡較差的情況下正常通路
- 類似應用 - 由于是在 App Shell 模型基礎上開發,因為應具有 Native App 的互動和導航,給使用者 Native App 的體驗
- 持續更新 - 始終是最新的,無版本和更新問題
- 安全 - 通過 HTTPS 協定提供服務,防止窺探和確定内容不被篡改
- 可索引 - 應用清單檔案和 Service Worker 可以讓搜尋引擎索引到,進而将其識别為『應用』
- 粘性 - 通過推送離線通知等,可以讓使用者回流
- 可安裝 - 使用者可以添加常用的 webapp 到桌面,免去去應用商店下載下傳的麻煩
- 可連結 - 通過連結即可分享内容,無需下載下傳安裝
manifest 的目的是将Web應用程式安裝到裝置的主螢幕,為使用者提供更快的通路和更豐富的體驗。
為了這樣做,我們需要添加一個manifest.json檔案并且在index.html檔案中進行聲明
pwa-manifest-webpack-plugin能夠讓我們在應用建構的時候生成檔案:
npm i pwa-manifest-webpack-plugin --save
我們接着能夠通過編輯build/webpack.dev.conf.js 以及build/webpack.prod.conf.js來更新建構過程。
在頂部引入pwa-manifest-webpack-plugin :
const manifestPlugin = require('pwa-manifest-webpack-plugin')
将它添加到插件:
plugins: [
new manifestPlugin({
name: '程式員', // 标題 指定了Web App的名稱。
short_name: '程式員', // 短标題 short_name其實是該應用的一個簡稱。一般來說,當沒有足夠空間展示應用的name時,系統就會使用short_name。
description: '程式員', // 這個字段的含義非常簡單,就是一段對該應用的描述。
display: 'standalone', // fullscreen:全屏顯示,會盡可能将所有的顯示區域都占滿;standalone:獨立應用模式,這種模式下打開的應用有自己的啟動圖示,并且不會有浏覽器的位址欄。是以看起來更像一個Native App;minimal-ui:與standalone相比,該模式會多出位址欄;browser:一般來說,會和正常使用浏覽器打開樣式一緻。
start_url: '/', // 這個屬性指定了使用者打開該Web App時加載的URL。相對URL會相對于manifest。這裡我們指定了start_url為/,通路根目錄。
orientation: 'portrait-primary', // 控制Web App的方向。設定某些值會具有類似鎖屏的效果(禁止旋轉),例如例子中的portrait-primary。具體的值包括:any, natural, landscape, landscape-primary, landscape-secondary, portrait, portrait-primary, portrait-secondary。
icon: {
// icons本身是一個數組,每個元素包含三個屬性:
//
// sizes:圖示的大小。通過指定大小,系統會選取最合适的圖示展示在相應位置上。
// src:圖示的檔案路徑。注意相對路徑是相對于manifest。
// type:圖示的圖檔類型
src: path.resolve('src/assets/logo.png'),
sizes: [200]
},
background_color: '#2d8cf0', // background_color是在應用的樣式資源為加載完畢前的預設背景,是以會展示在開屏界面。background_color加上我們剛才定義的icons就組成了Web App打開時的“開屏圖”。
theme_color: '#2d8cf0' // 定義應用程式的預設主題顔色。 這有時會影響作業系統顯示應用程式的方式(例如,在Android的任務切換器上,主題顔色包圍應用程式)。此外,還可以在meta标簽中設定theme_color:<meta name="theme-color" content="#5eace0"/>
})
]
最後,在 index.html中聲明使用manifest.json:
在 index.html中聲明使用manifest.json:
<link rel="manifest" href="./manifest.json" target="_blank" rel="external nofollow" >
<!-- 針對safari(iOS)的添加到桌面功能進行相關設定 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="程式員">
<link rel="apple-touch-icon" href="./static/images/logo.png" target="_blank" rel="external nofollow" >
<!--end-->
<!-- IE的設定 -->
<meta name="application-name" content="程式員" />
<meta name="msapplication-TileColor" content="#222">
<meta name="msapplication-square70x70logo" content="./static/images/logo.png" />
<meta name="msapplication-square150x150logo" content="./static/images/logo.png" />
<meta name="msapplication-square310x310logo" content="./static/images/logo.png" />
<!--end-->
配置完上面的步驟之後,你可能需要重新開機一下 npm run dev
service worker 是一個 WEB API,是 Web Workers 的一種實作,功能是可以攔截、處理請求,配合 CacheStorage 等 api,可以做到将資源存儲到本地等,實作離線通路。
service workers 的生命周期有:
- 安裝 install
- 激活 activate
建立檔案service-worker.js
如何把vue項目改造成支援PWA的功能(第二章:加入service-worker)
Mixins(混入)
Mixins 有點像是函數或者是宏,當某段 CSS 經常需要在多個元素中使用時,可以為這些共用的 CSS 定義一個 Mixin,然後隻需要在需要引用這些 CSS 地方調用該 Mixin 即可。
sass中可用mixin定義一些代碼片段,且可傳參數,友善日後根據需求調用。比如說處理css3浏覽器字首:
@mixin error($borderWidth: 2px) {
border: $borderWidth solid #F00;
color: #F00;
}
.generic-error {
padding: 20px;
margin: 4px;
@ include error(); //這裡調用預設 border: 2px solid #F00;
}
.login-error {
left: 12px;
position: absolute;
top: 20px;
@ include error(5px); //這裡調用 border:5px solid #F00;
}
在sass中,還支援條件語句:
@if可一個條件單獨使用,也可以和@else結合多條件使用
代碼如下:
$lte7: true;
$type: monster;
.ib{
display:inline-block;
@if $lte7 {
*display:inline;
*zoom:1;
}
}
p {
@if $type == ocean {
color: blue;
} @else if $type == matador {
color: red;
} @else if $type == monster {
color: green;
} @else {
color: black;
}
}
差別
- node-sass 是用 node(調用 cpp 編寫的 libsass)來編譯 sass;dart-sass 是用 drat VM 來編譯 sass;
- node-sass是自動編譯實時的,dart-sass需要儲存後才會生效 推薦 dart-sass 性能更好(也是 sass 官方使用的),而且 node-sass 因為國情問題經常裝不上
ESLint with error prevention only
- 隻配置使用 ESLint 官網的推薦規則
- 這些規則在這裡添加連結描述 ESLint + Airbnb config
- 使用 ESLint 官網推薦的規則 + Airbnb 第三方的配置
- Airbnb 的規則在這裡添加連結描述 ESLint + Standard config
- 使用 ESLint 官網推薦的規則 + Standard 第三方的配置
- Standard 的規則在這裡 添加連結描述 ESLint + Prettier
- 使用 ESLint 官網推薦的規則 + Prettier 第三方的配置
- Prettier 主要是做風格統一。代碼格式化工具
? Pick a unit testing solution: (Use arrow keys)
> Mocha + Chai //mocha靈活,隻提供簡單的測試結構,如果需要其他功能需要添加其他庫/插件完成。必須在全局環境中安裝
Jest //安裝配置簡單,容易上手。内置Istanbul,可以檢視到測試覆寫率,相較于Mocha:配置簡潔、測試代碼簡潔、易于和babel內建、内置豐富的expect
image.png
$ yarn config get registry
https://registry.yarnpkg.com
$ npm config get registry
https://registry.npmjs.org/
$ yarn config get registry
https://registry.npm.taobao.org
$ cnpm config get registry
https://registry.nlark.com/
[click.left&contextmenu&click.middle]
<div id="ask"><!--vue不能控制body和html的标簽-->
<!--滑鼠左鍵-->
<div :style="left_style" @click.left="mouseclick('左')"></div>
<!--滑鼠中鍵-->
<div :style="middle_style" @click.middle="mouseclick('中')"></div>
<!--滑鼠右鍵-->
<!--加prevent為了屏蔽浏覽器自帶右鍵-->
<div :style="right_style" @contextmenu.prevent="mouseclick('右')"></div>
</div>
安裝:
npm install -g @vue/cli
# OR
yarn global add @vue/cli
建立一個項目:
vue create my-project
# OR
vue ui
vue-cil是vue的腳手架工具)
$ npm install -g vue-cli
建立一個自己的vue項目
$ vue init webpack vuedemo
Vue CLI 2
npm install -g @vue/cli-init
# `vue init` 的運作效果将會跟 `[email protected]` 相同
vue init webpack my-project
1632819948.png
1632820571(1).png
$ yarn config get registry
https://registry.npm.taobao.org
$ yarn config set registry https://registry.npmjs.org --global
yarn config v1.22.11
success Set "registry" to "https://registry.npmjs.org".
Done in 0.06s.
$ cnpm config get registry
https://registry.nlark.com/
@vue-cli3建立項目報錯:ERROR command failed: npm install --loglevel error --registry=https://registry.npm.taobao.org --di
image.png
{
"useTaobaoRegistry": false,
"presets": {
"jeskson": {
"useConfigFiles": true,
"plugins": {
"@vue/cli-plugin-babel": {},
"@vue/cli-plugin-typescript": {
"classComponent": true,
"useTsWithBabel": true
},
"@vue/cli-plugin-router": {
"historyMode": true
},
"@vue/cli-plugin-vuex": {},
"@vue/cli-plugin-eslint": {
"config": "base",
"lintOn": [
"save"
]
},
"@vue/cli-plugin-unit-mocha": {},
"@vue/cli-plugin-e2e-cypress": {}
},
"vueVersion": "2",
"cssPreprocessor": "dart-sass"
}
},
"packageManager": "yarn"
}
[Error: unable to get local issuer certificate while running yarn command]
yarn config set "strict-ssl" false -g
$ yarn config list
yarn config v1.22.11
info yarn config
{
'version-tag-prefix': 'v',
'version-git-tag': true,
'version-commit-hooks': true,
'version-git-sign': false,
'version-git-message': 'v%s',
'init-version': '1.0.0',
'init-license': 'MIT',
'save-prefix': '^',
'bin-links': true,
'ignore-scripts': false,
'ignore-optional': false,
registry: 'https://registry.npm.taobao.org',
'strict-ssl': false,
'user-agent': 'yarn/1.22.11 npm/? node/v14.17.3 win32 x64',
lastUpdateCheck: 1632822666163
}
info npm config
{
cache: 'C:\\vue\\node_cache',
prefix: 'c:\\vue\\node_global',
ca: [
''
],
'strict-ssl': false,
registry: 'https://registry.npm.taobao.org/',
get: 'registry'
}
Done in 0.16s.
npm config set registry https://registry.npm.taobao.org
$ yarn config get registry
https://registry.npmjs.org
npm i 選項–global,–save,–save-dev
-global: 簡寫 -g
npm i express -g 為全局安裝,這種就可以直接使用express指令, 否則會提示express不是内部或外部指令
-save: 簡寫 -S, 作用是在package.json的dependencies字段增加或修改安裝包和版本号
-save-dev: 簡寫 -D, 是修改devDependencies, 這樣就不用安裝了某個包之後手動修改package.json
~ 與 ^ 版本
版本分為: 主版本号、次版本号、更新檔版本号
"devDependencies": {
"vue": "~2.2.2", // 比對最近小版本,如,會比對所有的2.2.x版本,但最高不會比對2.3.0
"vue-router": "^2.2.0" // 最近的一個大版本,所有 2.x.x但不不包括3.0.0,相當于 2.0.0 <= version < 3.0.0
}
詳細介紹了npm的使用,以及參數相關配置
https://javascript.ruanyifeng.com/nodejs/npm.html
vue-cli-service serve
指令會啟動一個開發伺服器 (基于 [webpack-dev-server]) 并附帶開箱即用的子產品熱重載 (Hot-Module-Replacement)。
使用
vue.config.js
裡的 [devServer] 字段配置開發伺服器。
vue-cli-service build
會在
dist/
目錄産生一個可用于生産環境的包,帶有 JS/CSS/HTML 的壓縮,和為更好的緩存而做的自動的 vendor chunk splitting。它的 chunk manifest 會内聯在 HTML 裡。
@vue/cli-plugin-eslint
會注入
vue-cli-service lint
指令
緩存和并行處理
-
會預設為 Vue/Babel/TypeScript 編譯開啟。檔案會緩存在cache-loader
中——如果你遇到了編譯方面的問題,記得先删掉緩存目錄之後再試試看。node_modules/.cache
-
會在多核 CPU 的機器上為 Babel/TypeScript 轉譯開啟。thread-loader
Git Hook
在安裝之後,
@vue/cli-service
也會安裝 [yorkie],它會讓你在
package.json
的
gitHooks
字段中友善地指定 Git hook:
{
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": [
"vue-cli-service lint",
"git add"
]
}
}
浏覽器相容性
browserslist
你會發現有
package.json
檔案裡的
browserslist
字段 (或一個單獨的
.browserslistrc
檔案),指定了項目的目标浏覽器的範圍。這個值會被 [@babel/preset-env] 和 [Autoprefixer]用來确定需要轉譯的 JavaScript 特性和需要添加的 CSS 浏覽器字首。
Polyfill
useBuiltIns: 'usage'
一個預設的 Vue CLI 項目會使用 [@vue/babel-preset-app],它通過
@babel/preset-env
和
browserslist
配置來決定項目需要的 polyfill。
預設情況下,它會把 [
useBuiltIns: 'usage'
] 傳遞給
@babel/preset-env
,這樣它會根據源代碼中出現的語言特性自動檢測需要的 polyfill。這確定了最終包裡 polyfill 數量的最小化。然而,這也意味着如果其中一個依賴需要特殊的 polyfill,預設情況下 Babel 無法将其檢測出來。
如果有依賴需要 polyfill,你有幾種選擇:
- 如果該依賴基于一個目标環境不支援的 ES 版本撰寫: 将其添加到
中的 [vue.config.js
]選項。這會為該依賴同時開啟文法轉換和根據使用情況檢測 polyfill。transpileDependencies
- 如果該依賴傳遞了 ES5 代碼并顯式地列出了需要的 polyfill: 你可以使用
的 [polyfills]選項預包含所需要的 polyfill。注意@vue/babel-preset-app
将被預設包含,因為現在的庫依賴 Promise 是非常普遍的。es.promise
// babel.config.js
module.exports = {
presets: [
['@vue/app', {
polyfills: [
'es.promise',
'es.symbol'
]
}]
]
}
- 如果該依賴傳遞 ES5 代碼,但使用了 ES6+ 特性且沒有顯式地列出需要的 polyfill (例如 Vuetify):請使用
然後在入口檔案添加useBuiltIns: 'entry'
。這會根據import 'core-js/stable'; import 'regenerator-runtime/runtime';
目标導入所有 polyfill,這樣你就不用再擔心依賴的 polyfill 問題了,但是因為包含了一些沒有用到的 polyfill 是以最終的包大小可能會增加。browserslist
建構庫或是 Web Component 時的 Polyfills
當使用 Vue CLI 來[建構一個庫或是 Web Component]時,推薦給
@vue/babel-preset-app
傳入
useBuiltIns: false
選項。這能夠確定你的庫或是元件不包含不必要的 polyfills。通常來說,打包 polyfills 應當是最終使用你的庫的應用的責任。
現代模式
有了 Babel 我們可以兼顧所有最新的 ES2015+ 語言特性,但也意味着我們需要傳遞轉譯和 polyfill 後的包以支援舊浏覽器。這些轉譯後的包通常都比原生的 ES2015+ 代碼會更冗長,運作更慢。現如今絕大多數現代浏覽器都已經支援了原生的 ES2015,是以因為要支援更老的浏覽器而為它們傳遞笨重的代碼是一種浪費。
Vue CLI 提供了一個“現代模式”幫你解決這個問題。以如下指令為生産環境建構:
vue-cli-service build --modern
Vue CLI 會産生兩個應用的版本:一個現代版的包,面向支援 [ES modules]的現代浏覽器,另一個舊版的包,面向不支援的舊浏覽器。
- 現代版的包會通過
在被支援的浏覽器中加載;它們還會使用<script type="module">
進行預加載。<link rel="modulepreload">
- 舊版的包會通過
加載,并會被支援 ES modules 的浏覽器忽略。<script nomodule>
- 一個針對 Safari 10 中
的修複會被自動注入。<script nomodule>
對于一個 Hello World 應用來說,現代版的包已經小了 16%。在生産環境下,現代版的包通常都會表現出顯著的解析速度和運算速度,進而改善應用的加載性能。
HTML
Index 檔案
public/index.html
檔案是一個會被 [html-webpack-plugin]處理的模闆。在建構過程中,資源連結會被自動注入。另外,Vue CLI 也會自動注入 resource hint (
preload/prefetch
、manifest 和圖示連結 (當用到 PWA 插件時) 以及建構過程中處理的 JavaScript 和 CSS 檔案的資源連結。
插值
因為 index 檔案被用作模闆,是以你可以使用 [lodash template] 文法插入内容:
-
用來做不轉義插值;<%= VALUE %>
-
用來做 HTML 轉義插值;<%- VALUE %>
-
用來描述 JavaScript 流程控制。<% expression %>
除了[被
html-webpack-plugin
暴露的預設值]之外,所有[用戶端環境變量]也可以直接使用。例如,
BASE_URL
的用法:
<link rel="icon" href="<%= BASE_URL %>favicon.ico" target="_blank" rel="external nofollow" >
Preload
[
<link rel="preload">
] 是一種 resource hint,用來指定頁面加載後很快會被用到的資源,是以在頁面加載的過程中,我們希望在浏覽器開始主體渲染之前盡早 preload。
預設情況下,一個 Vue CLI 應用會為所有初始化渲染需要的檔案自動生成 preload 提示。
這些提示會被 [@vue/preload-webpack-plugin]注入,并且可以通過
chainWebpack
的
config.plugin('preload')
進行修改和删除。
Prefetch
[
<link rel="prefetch">
]是一種 resource hint,用來告訴浏覽器在頁面加載完成後,利用空閑時間提前擷取使用者未來可能會通路的内容。
預設情況下,一個 Vue CLI 應用會為所有作為 async chunk 生成的 JavaScript 檔案 ([通過動态
import()
按需 code splitting]的産物) 自動生成 prefetch 提示。
這些提示會被 [@vue/preload-webpack-plugin] 注入,并且可以通過
chainWebpack
的
config.plugin('prefetch')
進行修改和删除。
示例:
// vue.config.js
module.exports = {
chainWebpack: config => {
// 移除 prefetch 插件
config.plugins.delete('prefetch')
// 或者
// 修改它的選項:
config.plugin('prefetch').tap(options => {
options[0].fileBlacklist = options[0].fileBlacklist || []
options[0].fileBlacklist.push(/myasyncRoute(.)+?.js$/)
return options
})
}
}
當 prefetch 插件被禁用時,你可以通過 webpack 的内聯注釋手動標明要提前擷取的代碼區塊:
import(/* webpackPrefetch: true */ './someAsyncComponent.vue')
webpack 的運作時會在父級區塊被加載之後注入 prefetch 連結。
提示
Prefetch 連結将會消耗帶寬。如果你的應用很大且有很多 async chunk,而使用者主要使用的是對帶寬較敏感的移動端,那麼你可能需要關掉 prefetch 連結并手動選擇要提前擷取的代碼區塊。
不生成 index
當基于已有的後端使用 Vue CLI 時,你可能不需要生成
index.html
,這樣生成的資源可以用于一個服務端渲染的頁面。這時可以向 [
vue.config.js
]加入下列代碼:
// vue.config.js
module.exports = {
// 去掉檔案名中的 hash
filenameHashing: false,
// 删除 HTML 相關的 webpack 插件
chainWebpack: config => {
config.plugins.delete('html')
config.plugins.delete('preload')
config.plugins.delete('prefetch')
}
}
然而這樣做并不是很推薦,因為:
- 寫死的檔案名不利于實作高效率的緩存控制。
- 寫死的檔案名也無法很好的進行 code-splitting (代碼分段),因為無法用變化的檔案名生成額外的 JavaScript 檔案。
- 寫死的檔案名無法在[現代模式]下工作。
你應該考慮換用 [indexPath] 選項将生成的 HTML 用作一個服務端架構的視圖模闆。
處理靜态資源
靜态資源可以通過兩種方式進行處理:
- 在 JavaScript 被導入或在 template/CSS 中通過相對路徑被引用。這類引用會被 webpack 處理。
- 放置在
目錄下或通過絕對路徑被引用。這類資源将會直接被拷貝,而不會經過 webpack 的處理。public
從相對路徑導入
當你在 JavaScript、CSS 或
*.vue
檔案中使用相對路徑 (必須以
.
開頭) 引用一個靜态資源時,該資源将會被包含進入 webpack 的依賴圖中。在其編譯過程中,所有諸如
<img src="...">
、
background: url(...)
和 CSS
@import
的資源 URL 都會被解析為一個子產品依賴。
例如,
url(./image.png)
會被翻譯為
require('./image.png')
,而:
<img src="./image.png">
将會被編譯到:
h('img', { attrs: { src: require('./image.png') }})
在其内部,我們通過
file-loader
用版本哈希值和正确的公共基礎路徑來決定最終的檔案路徑,再用
url-loader
将小于 4kb 的資源内聯,以減少 HTTP 請求的數量。
你可以通過 [chainWebpack]調整内聯檔案的大小限制。例如,下列代碼會将其限制設定為 10kb:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => Object.assign(options, { limit: 10240 }))
}
}
URL 轉換規則
- 如果 URL 是一個絕對路徑 (例如
),它将會被保留不變。/images/foo.png
- 如果 URL 以
開頭,它會作為一個相對子產品請求被解釋且基于你的檔案系統中的目錄結構進行解析。.
- 如果 URL 以
開頭,其後的任何内容都會作為一個子產品請求被解析。這意味着你甚至可以引用 Node 子產品中的資源:~
<img src="~some-npm-package/foo.png">
- 如果 URL 以
開頭,它也會作為一個子產品請求被解析。它的用處在于 Vue CLI 預設會設定一個指向@
的别名<projectRoot>/src
。(僅作用于模版中)@
Vue CLI 項目天生支援 [PostCSS]、[CSS Modules]和包含 [Sass]、[Less]、[Stylus] 在内的預處理器。
webpack 相關
// vue.config.js
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// 為生産環境修改配置...
} else {
// 為開發環境修改配置...
}
}
}
模式是 Vue CLI 項目中一個重要的概念。預設情況下,一個 Vue CLI 項目有三個模式:
-
模式用于development
vue-cli-service serve
-
模式用于test
vue-cli-service test:unit
-
模式用于production
和vue-cli-service build
vue-cli-service test:e2e
Vue CLI 啟動時已經存在的環境變量擁有最高優先級,并不會被
.env
檔案覆寫。
.env
環境檔案是通過運作
vue-cli-service
指令載入的,是以環境檔案發生變化,你需要重新開機服務。
-
會加載可能存在的vue-cli-service build
、.env
和.env.production
檔案然後建構出生産環境應用。.env.production.local
-
會在 staging 模式下加載可能存在的vue-cli-service build --mode staging
、.env
和.env.staging
檔案然後建構出生産環境應用。.env.staging.local
你可以在
vue.config.js
檔案中計算環境變量。它們仍然需要以
VUE_APP_
字首開頭。這可以用于版本資訊:
process.env.VUE_APP_VERSION = require('./package.json').version
module.exports = {
// config
}
隻在本地有效的變量
有的時候你可能有一些不應該送出到代碼倉庫中的變量,尤其是當你的項目托管在公共倉庫時。這種情況下你應該使用一個
.env.local
檔案取而代之。本地環境檔案預設會被忽略,且出現在
.gitignore
中。
.local
也可以加在指定模式的環境檔案上,比如
.env.development.local
将會在 development 模式下被載入,且被 git 忽略。
在本地預覽生産環境建構最簡單的方式就是使用一個 Node.js 靜态檔案伺服器
npm install -g serve
# -s 參數的意思是将其架設在 Single-Page Application 模式下
# 這個模式會處理即将提到的路由問題
serve -s dist
部署 VuePress 到 GitHub Pages
初始化本地項目,将 VuePress 作為本地依賴安裝:
# 初始化項目
cd ~/Desktop
mkdir my-vuepress
cd my-vuepress
npm init -y
# 将 VuePress 作為一個本地依賴安裝
yarn add -D vuepress # 或者:npm install -D vuepress
# 建立一個 docs 檔案夾
mkdir docs
# 建立一個 markdown 檔案
echo '# Hello VuePress!' > docs/README.md
接着,在
package.json
裡加一些腳本:
{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs",
"deploy-gh": "GH=1 yarn docs:build && bash scripts/deploy-gh.sh"
}
}
運作本地開發環境:
yarn docs:dev # 或者:npm run docs:dev
或建構線上靜态檔案:
yarn docs:build # 或者:npm run docs:build
在
docs/.vuepress/config.js
中配置正确的
base
。
module.exports = {
title: "My Blog",
description: "This is a blog.",
base: '/blog/'
}
建立腳本
my-vuepress/scripts/deploy-gh.sh
#!/usr/bin/env sh
# 確定腳本抛出遇到的錯誤
set -e
# 生成靜态檔案
npm run docs:build
# 進入生成的檔案夾
cd docs/.vuepress/dist
# 如果是釋出到自定義域名
# echo 'www.example.com' > CNAME
git init
git add -A
git commit -m 'deploy'
# 如果釋出到 https://<USERNAME>.github.io
# git push -f [email protected]:<USERNAME>/<USERNAME>.github.io.git master
# 如果釋出到 https://<USERNAME>.github.io/<REPO>
# git push -f [email protected]:<USERNAME>/<REPO>.git master:gh-pages
# 把上面的 <USERNAME> 換成你自己的 Github 使用者名,<REPO> 換成倉庫名,比如我這裡就是:
git push -f [email protected]:wtyqer/blog.git master:gh-pages
cd -
執行腳本進行部署:
yarn deploy-gh # 或者:npm run deploy-gh
安裝node.js
官網:https://nodejs.org/en/download/
曆史版本:https://nodejs.org/en/download/releases/
安裝node,建議不要安裝在系統盤(如C:)
在nodejs安裝路徑下,建立node_global和node_cache兩個檔案夾
設定nodejs prefix(全局)和cache(緩存)路徑
$ npm config get registry
https://registry.npm.taobao.org/
$ cnpm config get registry
https://registry.nlark.com/
$ yarn config get registry
https://registry.npm.taobao.org
基于 Node.js 安裝cnpm(淘寶鏡像)
npm install -g cnpm --registry=https://registry.npm.taobao.org
修改系統變量PATH
image.png
新增系統變量NODE_PATH
image.png
baseUrl
從 Vue CLI 3.3 起已棄用,請使用[
publicPath
]
publicPath
- Type:
string
- Default:
部署應用包時的基本 URL。'/'
outputDir
- Type:
string
- Default:
當運作'dist'
時生成的生産環境建構檔案的目錄。注意目标目錄在建構之前會被清除 (建構時傳入vue-cli-service build
可關閉該行為)。--no-clean
assetsDir
- Type:
string
- Default:
放置生成的靜态資源 (js、css、img、fonts) 的 (相對于''
的) 目錄。outputDir
indexPath
- Type:
string
- Default:
指定生成的'index.html'
的輸出路徑 (相對于index.html
)。也可以是一個絕對路徑。outputDir
pages
module.exports = {
pages: {
index: {
// page 的入口
entry: 'src/index/main.js',
// 模闆來源
template: 'public/index.html',
// 在 dist/index.html 的輸出
filename: 'index.html',
// 當使用 title 選項時,
// template 中的 title 标簽需要是 <title><%= htmlWebpackPlugin.options.title %></title>
title: 'Index Page',
// 在這個頁面中包含的塊,預設情況下會包含
// 提取出來的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 當使用隻有入口的字元串格式時,
// 模闆會被推導為 `public/subpage.html`
// 并且如果找不到的話,就回退到 `public/index.html`。
// 輸出檔案名會被推導為 `subpage.html`。
subpage: 'src/subpage/main.js'
}
}
lintOnSave
- Type:
|boolean
|'warning'
|'default'
'error'
- Default:
'default'
設定讓浏覽器 overlay 同時顯示警告和錯誤:
// vue.config.js
module.exports = {
devServer: {
overlay: {
warnings: true,
errors: true
}
}
}
當
lintOnSave
是一個 truthy 的值時,
eslint-loader
在開發和生産建構下都會被啟用。如果你想要在生産建構時禁用
eslint-loader
,你可以用如下配置:
// vue.config.js
module.exports = {
lintOnSave: process.env.NODE_ENV !== 'production'
}
transpileDependencies
- Type:
Array<string | RegExp>
- Default:
預設情況下[]
會忽略所有babel-loader
中的檔案。如果你想要通過 Babel 顯式轉譯一個依賴,可以在這個選項中列出來。node_modules
productionSourceMap
- Type:
boolean
- Default:
如果你不需要生産環境的 source map,可以将其設定為true
以加速生産環境建構。false
configureWebpack
- Type:
Object | Function
如果這個值是一個對象,則會通過 [webpack-merge]合并到最終的配置中。
如果這個值是一個函數,則會接收被解析的配置作為參數。該函數既可以修改配置并不傳回任何東西,也可以傳回一個被克隆或合并過的配置版本。
chainWebpack
- Type:
是一個函數,會接收一個基于 webpack-chain 的Function
執行個體。允許對内部的 webpack 配置進行更細粒度的修改。ChainableConfig
devServer.proxy
可以是一個指向開發環境 API 伺服器的字元串:
module.exports = {
devServer: {
proxy: 'http://localhost:4000'
}
}
module.exports = {
devServer: {
proxy: {
'/api': {
target: '<url>',
ws: true,
changeOrigin: true
},
'/foo': {
target: '<other_url>'
}
}
}
}
在普通webpack 中的配置, 可以同時使用 多個預處理器。
module.exports = {
// ...
module: {
rules: [{
test: /.less$/,
use: ['style-loader', 'less-loader', 'less-loader', {
loader: 'style-resources-loader',
options: {
patterns: [ // 隻有一條時也可以寫成對象形式
path.resolve(__dirname, 'path/to/scss/variables/*.less'),
path.resolve(__dirname, 'path/to/scss/mixins/*.less'),
],
injector: 'append' // 如果在樣式檔案之後導入就加此行配置
}
}]
}]
},
// ...
}
pluginOptions
- Type:
這是一個不進行任何 schema 驗證的對象,是以它可以用來傳遞任何第三方插件選項。例如:Object
module.exports = { pluginOptions: { foo: { // 插件可以作為 `options.pluginOptions.foo` 通路這些選項。 } } }
style樣式資源處理器,在style資源中注入内容,導入css / sass / scss / less / stylus這些内容
主要作用
導入一些公共的樣式檔案,比如:variables / mixins / functions,避免在每個樣式檔案中手動的@import導入
浏覽器的同源政策
就是兩個頁面具有相同的 協定protocol , 主機host , 端口号port
請求一個接口時,出現 Access-Control-Allow-Origin 等 就是說明請求跨域了
vue中解決跨域:配置vue.config.js檔案,如果沒有就自己建一個
原理:
- 将域名發送給本地的伺服器localhost:8080
- 再由本地的伺服器去請求真正的伺服器
- 因為請求是從服務端發出的,是以就不存在跨域的問題了
修改vue.config.js檔案 需要重新開機服務
module.exports = {
devServer: {
// 跨域
proxy: {
'/api': {
// 目标路徑
target: 'https://www.bilibili.com/',
// 允許跨域
changeOrigin: true,
// 重寫路徑
pathRewrite: {
'^/api': ''
}
}
}
}
}
前端工程化、子產品化、元件化、自動化、規範化
所謂前端工程化,我認為就是将前端項目當成一項系統工程進行分析、組織和建構進而達到項目結構清晰、分工明确、團隊配合默契、開發效率提高的目的。
模闆化是在檔案層面上,對代碼和資源的拆分。就是将一個大檔案拆分成互相依賴的小檔案,再進行統一的拼裝和加載。
“簡單重複的工作交給機器來做”,自動化也就是有很多自動化工具(
glup、webpack
)代替我們來完成,例如持續內建、自動化建構、自動化部署、自動化測試等等。
目錄結構的制定、編碼規範、前後端接口規範、文檔規範、元件管理、代碼包管理(
SVN、Git
)、
commit
送出代碼備注描述規範、定期
codeReview
、視覺圖示規範
如果您在聲明元件時更喜歡基于類的 API,則可以使用官方維護的 [vue-class-component]裝飾器:
import Vue from 'vue'
import Component from 'vue-class-component'
// @Component 修飾符注明了此類為一個 Vue 元件
@Component({
// 所有的元件選項都可以放在這裡
template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
// 初始資料可以直接聲明為執行個體的 property
message: string = 'Hello!'
// 元件方法也可以直接聲明為執行個體的方法
onClick (): void {
window.alert(this.message)
}
}
[增強類型以配合插件使用]
TypeScript 有一個特性來補充現有的類型,叫做[子產品補充]
聲明一個
string
類型的執行個體 property
$myProperty
:
// 1. 確定在聲明補充的類型之前導入 'vue'
import Vue from 'vue'
// 2. 定制一個檔案,設定你想要補充的類型
// 在 types/vue.d.ts 裡 Vue 有構造函數類型
declare module 'vue/types/vue' {
// 3. 聲明為 Vue 補充的東西
interface Vue {
$myProperty: string
}
}
在你的項目中包含了上述作為聲明檔案的代碼之後 (像
my-property.d.ts
),你就可以在 Vue 執行個體上使用
$myProperty
了。
var vm = new Vue()
console.log(vm.$myProperty) // 将會順利編譯通過
你也可以聲明額外的 property 群組件選項:
import Vue from 'vue'
declare module 'vue/types/vue' {
// 可以使用 `VueConstructor` 接口
// 來聲明全局 property
interface VueConstructor {
$myGlobal: string
}
}
// ComponentOptions 聲明于 types/options.d.ts 之中
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
myOption?: string
}
}
上述的聲明允許下面的代碼順利編譯通過:
// 全局 property
console.log(Vue.$myGlobal)
// 額外的元件選項
var vm = new Vue({
myOption: 'Hello'
})
[标注傳回值]
因為 Vue 的聲明檔案天生就具有循環性,TypeScript 可能在推斷某個方法的類型的時候存在困難。是以,你可能需要在
render
或
computed
裡的方法上标注傳回值。
import Vue, { VNode } from 'vue'
const Component = Vue.extend({
data () {
return {
msg: 'Hello'
}
},
methods: {
// 需要标注有 `this` 參與運算的傳回值類型
greet (): string {
return this.msg + ' world'
}
},
computed: {
// 需要标注
greeting(): string {
return this.greet() + '!'
}
},
// `createElement` 是可推導的,但是 `render` 需要傳回值類型
render (createElement): VNode {
return createElement('div', this.greeting)
}
})
如果你發現類型推導或成員補齊不工作了,标注某個方法也許可以幫助你解決這個問題。使用
--noImplicitAny
選項将會幫助你找到這些未标注的方法。
[标注 Prop]
import Vue, { PropType } from 'vue'
interface ComplexMessage {
title: string,
okMessage: string,
cancelMessage: string
}
const Component = Vue.extend({
props: {
name: String,
success: { type: String },
callback: {
type: Function as PropType<() => void>
},
message: {
type: Object as PropType<ComplexMessage>,
required: true,
validator (message: ComplexMessage) {
return !!message.title;
}
}
}
})
Jest
Jest 是一個專注于簡易性的 JavaScript 測試架構。一個其獨特的功能是可以為測試生成快照 (snapshot),以提供另一種驗證應用單元的方法。
Mocha
Mocha 是一個專注于靈活性的 JavaScript 測試架構。因為其靈活性,它允許你選擇不同的庫來滿足諸如偵聽 (如 Sinon) 和斷言 (如 Chai) 等其它常見的功能。另一個 Mocha 獨特的功能是它不止可以在 Node.js 裡運作測試,還可以在浏覽器裡運作測試。
Vue Testing Library (@testing-library/vue)
Vue Testing Library 是一組專注于測試元件而不依賴實作細節的工具。由于在設計時就充分考慮了可通路性,它采用的方案也使重構變得輕而易舉。
它的指導原則是,與軟體使用方式相似的測試越多,它們提供的可信度就越高。
Vue Test Utils
Vue Test Utils 是官方的偏底層的元件測試庫,它是為使用者提供對 Vue 特定 API 的通路而編寫的。如果你對測試 Vue 應用不熟悉,建議你使用 Vue Testing Library,它是 Vue Test Utils 的抽象。
[端到端 (E2E) 測試]
端到端測試驗證應用中的所有層。這不僅包括你的前端代碼,還包括所有相關的後端服務和基礎設施,它們更能代表你的使用者所處的環境。通過測試使用者操作如何影響應用,端到端測試通常是提高應用是否正常運作的信心的關鍵。
Vue.js 生态系統中常用的端到端測試架構
Cypress.io
Cypress.io 是一個測試架構,旨在通過使開發者能夠可靠地測試他們的應用,同時提供一流的開發者體驗,來提高開發者的生産率。
Nightwatch.js
Nightwatch.js 是一個端到端測試架構,可用于測試 web 應用和網站,以及 Node.js 單元測試和內建測試。
Puppeteer
Puppeteer 是一個 Node.js 庫,它提供高階 API 來控制浏覽器,并可以與其他測試運作程式 (例如 Jest) 配對來測試應用。
TestCafe
TestCafe 是一個基于端到端的 Node.js 架構,旨在提供簡單的設定,以便開發者能夠專注于建立易于編寫和可靠的測試。
添加執行個體 property
你可能會在很多元件裡用到資料/實用工具,但是不想[污染全局作用域]。這種情況下,你可以通過在原型上定義它們使其在每個 Vue 的執行個體中可用。
Vue.prototype.$appName = 'My App'
這樣
$appName
就在所有的 Vue 執行個體中可用了,甚至在執行個體被建立之前就可以。如果我們運作:
new Vue({
beforeCreate: function () {
console.log(this.$appName)
}
})
則控制台會列印出
My App
。就這麼簡單!
[當沒有使用子產品系統時]
Object.freeze
,它做的事情是阻止這個對象在未來被修改。
1632987957.png
1632987983(1).png
SVG 圖示系統
https://github.com/sdras/vue-sample-svg-icons/。
1632988869(1).png
[在浏覽器中展示源代碼]
vue.config.js
内的
devtool
property:
module.exports = {
configureWebpack: {
devtool: 'source-map'
}
}
[Vue Devtools]
能夠為 Vuex 提供時間旅行式的調試體驗。
記憶體洩漏在 Vue 應用中通常不是來自 Vue 自身的,更多地發生于把其它庫內建到應用中的時候。
keep-alive
包裹一個元件後,它的狀态就會保留,是以就留在了記憶體裡。
當你用
keep-alive
包裹一個元件後,它的狀态就會保留,是以就留在了記憶體裡。
<button @click="show = false">Hide</button>
<keep-alive>
<!-- `<my-component>` 即便被删除仍會刻意保留在記憶體裡 -->
<my-component v-if="show"></my-component>
</keep-alive>
這個技巧可以用來提升使用者體驗。
activated
和
deactivated
deactivated: function () { // 移除任何你不想保留的資料 }
用戶端存儲
localStorage.name = this.name;
永遠不要把和
v-if
同時用在同一個元素上。
v-for
一般我們在兩種常見的情況下會傾向于這樣做:
- 為了過濾一個清單中的項目 (比如
)。在這種情形下,請将v-for="user in users" v-if="user.isActive"
替換為一個計算屬性 (比如users
),讓其傳回過濾後的清單。activeUsers
- 為了避免渲染本應該被隐藏的清單 (比如
)。這種情形下,請将v-for="user in users" v-if="shouldShowUsers"
移動至容器元素上 (比如v-if
、ul
)。ol
元件的 data
必須是一個函數。
當在元件中使用
data
property 的時候 (除了
new Vue
外的任何地方),它的值必須是傳回一個對象的函數。
當 data
的值是一個對象時,它會在這個元件的所有執行個體之間共享。
Prop 定義應該盡量詳細。
在你送出的代碼中,prop 的定義應該盡量詳細,至少需要指定其類型。
有兩個好處:
- 它們寫明了元件的 API,是以很容易看懂元件的用法;
- 在開發環境下,如果向一個元件提供格式不正确的 prop,Vue 将會告警,以幫助你捕獲潛在的錯誤來源。
1632994810(1).png
總是用配合
key
。
v-for
[為元件樣式設定作用域]scoped
隻應該擁有單個活躍執行個體的元件應該以
The
字首命名,以示其唯一性。
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
1632995507(1).png
<!-- 在單檔案元件、字元串模闆和 JSX 中 -->
<MyComponent/>
<!-- 在 DOM 模闆中 -->
<my-component></my-component>
<!-- 在單檔案元件和字元串模闆中 -->
<MyComponent/>
<!-- 在 DOM 模闆中 -->
<my-component></my-component>
或者
<!-- 在所有地方 -->
<my-component></my-component>
Vue.component('MyComponent', {
// ...
})
Vue.component('my-component', {
// ...
})
import MyComponent from './MyComponent.vue'
export default {
name: 'MyComponent',
// ...
}
props: {
greetingText: String
}
<WelcomeMessage greeting-text="hi"/>
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
<MyComponent
foo="a"
bar="b"
baz="c"
/>
1632995900(1).png
元素選擇器應該避免在 scoped
中出現。
在
scoped
樣式中,類選擇器比元素選擇器更好,因為大量使用元素選擇器是很慢的。
❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創作更好的文章
點贊、收藏和評論
我是
Jeskson
(達達前端),感謝各位人才的:點贊、收藏和評論,我們下期見!(如本文内容有地方講解有誤,歡迎指出☞謝謝,一起學習了)
我們下期見!
收錄,歡迎
github
:https://github.com/webVueBlog/WebFamily
Star