前言: 由於最近纔開始接觸Typescript,而後首先就是搭建環境,而網上教程時間比較久並且不全,因此在這裏記錄下手把手的詳細腳印。😂 🎉🎉🎉javascript
源碼地址請戳 👇👇👇 vue-ts-initcss
vue init webpack vue-ts-init
cd vue-ts-init
npm install
複製代碼
腳手架項目webpack版本爲3.6.0html
方法一:使用yarn安裝vue
yarn upgrade webpack@4.6.0
yarn add webpack-dev-server webpack-cli -D
複製代碼
方法二:手動修改package.json中webpack版本,從新安裝,完成後java
運行 npm run build
, 報錯node
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead
jquery
緣由:CommonsChunkPlugin
已被webpack4廢棄,推薦使用SplitChunkPlugin
抽離公共模塊webpack
解決:找到 /build/webpack.prod.conf.js ,去掉以下配置git
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
)
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
複製代碼
/build/webpack.prod.conf.js,添加es6
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
// 添加以下配置
optimization: {
splitChunks: {
cacheGroups: {
commons: {
chunks: "all",
minChunks: 2,
maxInitialRequests: 5, // The default limit is too small to showcase the effect
minSize: 0 // This is example is too small to create commons chunks
},
vendor: {
test: /node_modules/,
chunks: "all",
name: "vendor",
priority: 10,
enforce: true
}
}
}
},
......
})
複製代碼
附:官方的例子 common-chunk-and-vendor-chunk
optimization: {
//提取公共模塊,webpack4去除了CommonsChunkPlugin,使用SplitChunksPlugin做爲替代
//主要用於多頁面
//例子代碼 https://github.com/webpack/webpack/tree/master/examples/common-chunk-and-vendor-chunk
//SplitChunksPlugin配置,其中緩存組概念目前不是很清楚
splitChunks: {
// 表示顯示塊的範圍,有三個可選值:initial(初始塊)、async(按需加載塊)、all(所有塊),默認爲all;
chunks: "all",
// 表示在壓縮前的最小模塊大小,默認爲0;
minSize: 30000,
//表示被引用次數,默認爲1
minChunks: 1,
//最大的按需(異步)加載次數,默認爲1;
maxAsyncRequests: 3,
//最大的初始化加載次數,默認爲1;
maxInitialRequests: 3,
// 拆分出來塊的名字(Chunk Names),默認由塊名和hash值自動生成;設置ture則使用默認值
name: true,
//緩存組,目前在項目中設置cacheGroup能夠抽取公共模塊,不設置則不會抽取
cacheGroups: {
//緩存組信息,名稱能夠本身定義
commons: {
//拆分出來塊的名字,默認是緩存組名稱+"~" + [name].js
name: "test",
// 同上
chunks: "all",
// 同上
minChunks: 3,
// 若是cacheGroup中沒有設置minSize,則據此判斷是否使用上層的minSize,true:則使用0,false:使用上層minSize
enforce: true,
//test: 緩存組的規則,表示符合條件的的放入當前緩存組,值能夠是function、boolean、string、RegExp,默認爲空;
test:""
},
//設置多個緩存規則
vendor: {
test: /node_modules/,
chunks: "all",
name: "vendor",
//表示緩存的優先級
priority: 10,
enforce: true
}
}
}
}
複製代碼
再運行 npm run build
, 報錯
building for production.../Users/xyz_persist/front_end/ts/vue-ts-init2/node_modules/html-webpack-plugin/lib/compiler.js:81
var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, {
^
TypeError: compilation.mainTemplate.applyPluginsWaterfall is not a function
複製代碼
緣由:html-webpack-plugin
未升級版本致使
解決:升級 html-webpack-plugin
版本
npm i html-webpack-plugin@3.2.0
npm i vue-loader@15.7.1
複製代碼
再運行 npm run build
, 報錯
⠋ building for production...(node:13954) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
⠼ building for production...(node:13954) UnhandledPromiseRejectionWarning: Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
複製代碼
緣由:extract-text-webpack-plugin還不能支持webpack4.0.0以上的版本
解決:升級 extract-text-webpack-plugin
版本
npm i extract-text-webpack-plugin@next -D
注:extract-text-webpack-plugin做用:
將全部入口chunk中引用的.css抽離到獨立的css文件中,而再也不內嵌到JS bundle中。
若是樣式文件大小較大,會更快提早加載,由於
CSS bundle
會跟JS bundle
並行加載
再運行 npm run build
, 報錯
TypeError: Cannot read property 'eslint' of undefined
at Object.module.exports (/Users/xyz_persist/front_end/ts/vue-ts-init2/node_modules/eslint-loader/index.js:148:18)
TypeError: Cannot read property 'vue' of undefined
at Object.module.exports (/Users/xyz_persist/front_end/ts/vue-ts-init2/node_modules/vue-loader/lib/loader.js:61:18)
@ ./src/main.js 4:0-24 13:21-24
......
複製代碼
緣由:eslint-loader、vue-loader版本問題
解決:
npm i eslint-loader@2.2.1 -D
npm i vue-loader@15.7.1 -D
複製代碼
再運行 npm run build
, 報錯
ERROR in ./src/App.vue
Module Error (from ./node_modules/vue-loader/lib/index.js):
vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
@ ./src/main.js 4:0-24 13:21-24
複製代碼
緣由:Vue-loader在15.*以後的版本都是 vue-loader的使用都是須要伴生 VueLoaderPlugin的
解決:在/build/webpack.base.conf.js中添加
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
......
plugins: [
new VueLoaderPlugin()
],
}
複製代碼
再運行 npm run build
, 報錯
緣由:webpack4.x中提取CSS文件應該使用mini-css-extract-plugin
,廢棄extract-text-webpack-plugin
解決:在/build/webpack.prod.conf.js
中
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
plugins: [
...
// new ExtractTextPlugin({
// filename: utils.assetsPath('css/[name].[contenthash].css'),
// allChunks: true,
// }),
new MiniCssExtractPlugin({
filename: 'css/app.[name].css',
chunkFilename: 'css/app.[contenthash:12].css' // use contenthash *
}),
]
複製代碼
再修改/build/utils.js文件
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
if (options.extract) {
// return ExtractTextPlugin.extract({
// use: loaders,
// fallback: 'vue-style-loader'
// })
// MiniCssExtractPlugin.loader,
return [MiniCssExtractPlugin.loader].concat(loaders)
} else {
return ['vue-style-loader'].concat(loaders)
}
複製代碼
至此,npm run build
已經可以正常打包,生產環境搞定
運行 npm run dev
, 命令行沒有報錯
打開瀏覽器,控制檯報錯
BaseClient.js?e917:12 Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
at Module.eval (BaseClient.js?e917:12)
at eval (BaseClient.js:42)
at Module../node_modules/webpack-dev-server/client/clients/BaseClient.js (app.js:2061)
at __webpack_require__ (app.js:727)
at fn (app.js:101)
at Module.eval (SockJSClient.js?810f:26)
at eval (SockJSClient.js:137)
at Module../node_modules/webpack-dev-server/client/clients/SockJSClient.js (app.js:2073)
at __webpack_require__ (app.js:727)
at fn (app.js:101)
複製代碼
緣由:根據字面意思,猜想是解析JS的時候不認識 exports
解決:/build/webpack.base.conf.js 中
{
test: /\.js$/,
loader: 'babel-loader',
// include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')],
include: [resolve('src'), resolve('test')]
},
複製代碼
至此,npm run dev
編譯沒問題,開發環境搞定!
webpack4.x版本,其餘插件修改: NoEmitOnErrorsPlugin
廢棄,使用optimization.noEmitOnErrors
替代,在生產環境中默認開啓 ModuleConcatenationPlugin
廢棄,使用optimization.concatenateModules
替代,在生產環境默認開啓 NamedModulesPlugin
廢棄,使用optimization.namedModules
替代,在生產環境默認開啓 uglifyjs-webpack-plugin
升級到了v1.0版本,默認開啓緩存和並行功能 CommonsChunkPlugin
被刪除
webpack.optimize.CommonsChunkPlugin
,在Webpack 4.x中,咱們藉助於config.optimization
來實現webpack.optimize.UglifyJsPlugin
、 webpack.optimize.OptimizeCSSPlugin
,在Webpack 4.x中都取消了,可是可以使用UglifyJsPlugin
及OptimizeCSSPlugin
插件來代替extract-text-webpack-plugin
, 在webpack 4.x中則應該使用mini-css-extract-plugin
vue-loader
也必須更新到15.0以上版本npm i vue-class-component vue-property-decorator --save
npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev
複製代碼
這些庫大致的做用:
vue-class-component
上加強更多的結合 Vue 特性的裝飾器ts-loader
,其實就是爲了讓webpack識別 .ts .tsx文件.ts
.tsx
文件 約束代碼格式(做用等同於eslint)tslint
配置 standard
風格的約束找到/build/webpack.base.conf.js
entry.app
將main.js
改爲 main.ts
,順便把項目文件中的main.js
也改爲main.ts
, 裏面內容保持不變entry: {
app: './src/main.ts'
}
複製代碼
resolve.extensions
裏面加上.ts
後綴 (是爲了以後引入.ts的時候不寫後綴)resolve: {
extensions: ['.js', '.vue', '.json', '.ts'],
alias: {
'@': resolve('src')
}
}
複製代碼
module.rules
添加webpack對.ts
的解析module: {
rules: [
....
// 新增如下配置
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
"babel-loader",
{
loader: "ts-loader",
options: { appendTsxSuffixTo: [/\.vue$/] }
},
{
loader: 'tslint-loader'
}
]
},
....
}
}
複製代碼
注:ts-loader
會檢索當前目錄下的 tsconfig.json
文件,根據裏面定義的規則來解析.ts
文件(就跟.babelrc
的做用同樣),tslint-loader
做用等同於 eslint-loader
在根路徑下建立tsconfig.json
文件,完整配置見tsconfig.json
這是我本地的配置
{
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
// 解析非相對模塊名的基準目錄
"baseUrl": ".",
// 指定特殊模塊的路徑
"paths": {
"@/*": ["*", "src/*"]
},
"jsx": "preserve",
"jsxFactory": "h",
// 容許從沒有設置默認導出的模塊中默認導入
"allowSyntheticDefaultImports": true,
// 啓用裝飾器
"experimentalDecorators": true,
// 容許編譯javascript文件
"allowJs": true,
// 採用的模塊系統
"module": "esnext",
// 編譯輸出目標 ES 版本
"target": "es5",
// 如何處理模塊
"moduleResolution": "node",
// 將每一個文件做爲單獨的模塊
"isolatedModules": true,
// 編譯過程當中須要引入的庫文件的列表
"lib": [
"dom",
"es5",
"es6",
"es7",
"es2015.promise"
],
"sourceMap": true,
"pretty": true
}
}
複製代碼
貼一份參考的配置
{
// 編譯選項
"compilerOptions": {
// 輸出目錄
"outDir": "./output",
// 是否包含能夠用於 debug 的 sourceMap
"sourceMap": true,
// 以嚴格模式解析
"strict": true,
// 採用的模塊系統
"module": "esnext",
// 如何處理模塊
"moduleResolution": "node",
// 編譯輸出目標 ES 版本
"target": "es5",
// 容許從沒有設置默認導出的模塊中默認導入
"allowSyntheticDefaultImports": true,
// 將每一個文件做爲單獨的模塊
"isolatedModules": false,
// 啓用裝飾器
"experimentalDecorators": true,
// 啓用設計類型元數據(用於反射)
"emitDecoratorMetadata": true,
// 在表達式和聲明上有隱含的any類型時報錯
"noImplicitAny": false,
// 不是函數的全部返回路徑都有返回值時報錯。
"noImplicitReturns": true,
// 從 tslib 導入外部幫助庫: 好比__extends,__rest等
"importHelpers": true,
// 編譯過程當中打印文件名
"listFiles": true,
// 移除註釋
"removeComments": true,
"suppressImplicitAnyIndexErrors": true,
// 容許編譯javascript文件
"allowJs": true,
// 解析非相對模塊名的基準目錄
"baseUrl": "./",
// 指定特殊模塊的路徑
"paths": {
"jquery": [
"node_modules/jquery/dist/jquery"
]
},
// 編譯過程當中須要引入的庫文件的列表
"lib": [
"dom",
"es2015",
"es2015.promise"
]
}
}
複製代碼
在根路徑下建立tslint.json
文件,就是 引入 ts
的 standard
規範
{
"defaultSeverity": "error",
"extends": "tslint-config-standard",
"globals": {
"require": true
},
"rules": {
"space-before-function-paren": false,
"whitespace": [false],
"no-consecutive-blank-lines": false,
"no-angle-bracket-type-assertion": false,
"no-empty-character-class": false
}
}
複製代碼
因爲 TypeScript
默認並不支持 *.vue
後綴的文件,因此在 vue
項目中引入的時候須要建立一個 vue-shim.d.ts
文件,放在項目項目對應使用目錄下,例如 src/vue-shim.d.ts
// 意思是告訴 TypeScript *.vue 後綴的文件能夠交給 vue 模塊來處理
import Vue from "vue";
import lodash from "lodash";
// 重要***
declare module '*.vue' {
export default Vue
}
declare module 'vue/types/vue' {
interface Vue {
$Message: any,
$Modal: any
}
}
declare global {
const _: typeof lodash
}
複製代碼
而在代碼中導入 *.vue
文件的時候,須要寫上 .vue
後綴。緣由仍是由於 TypeScript
默認只識別 *.ts
文件,不識別 *.vue
文件
import HelloWorld from '@/components/HelloWorld.vue'
.vue
文件在這以前先讓咱們瞭解一下所須要的插件(下面的內容須要掌握es7
的裝飾器, 就是下面使用的@符號)
vue-class-component 對 Vue
組件進行了一層封裝,讓 Vue
組件語法在結合了 TypeScript
語法以後更加扁平化:
<template>
<div>
<input v-model="msg">
<p>msg: {{ msg }}</p>
<p>computed msg: {{ computedMsg }}</p>
<button @click="greet">Greet</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component
export default class App extends Vue {
// 初始化數據
msg = 123
// 聲明週期鉤子
mounted () {
this.greet()
}
// 計算屬性
get computedMsg () {
return 'computed ' + this.msg
}
// 方法
greet () {
alert('greeting: ' + this.msg)
}
}
</script>
複製代碼
上面的代碼跟下面的代碼做用是同樣的
export default {
data () {
return {
msg: 123
}
}
// 聲明週期鉤子
mounted () {
this.greet()
}
// 計算屬性
computed: {
computedMsg () {
return 'computed ' + this.msg
}
}
// 方法
methods: {
greet () {
alert('greeting: ' + this.msg)
}
}
}
複製代碼
vue-property-decorator 是在 vue-class-component
上加強了更多的結合 Vue
特性的裝飾器,新增了這 7 個裝飾器:
@Emit
@Inject
@Model
@Prop
@Provide
@Watch
@Component
(從 vue-class-component
繼承)在這裏列舉幾個經常使用的@Prop/@Watch/@Component
, 更多信息,詳見官方文檔
import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'
@Component
export class MyComponent extends Vue {
@Prop()
propA: number = 1
@Prop({ default: 'default value' })
propB: string
@Prop([String, Boolean])
propC: string | boolean
@Prop({ type: null })
propD: any
@Watch('child')
onChildChanged(val: string, oldVal: string) { }
}
複製代碼
上面的代碼至關於:
export default {
props: {
checked: Boolean,
propA: Number,
propB: {
type: String,
default: 'default value'
},
propC: [String, Boolean],
propD: { type: null }
}
methods: {
onChildChanged(val, oldVal) { }
},
watch: {
'child': {
handler: 'onChildChanged',
immediate: false,
deep: false
}
}
}
複製代碼
App.vue
文件script
標籤上加上 lang="ts"
, 意思是讓webpack
將這段代碼識別爲typescript
而非javascript
vue-property-decorator
語法改造以前代碼以下:
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({})
export default class App extends Vue {}
</script>
複製代碼
以下:
<template>
<div class="hello">
<h1>{{ message }}</h1>
<h2>Essential Links</h2>
<button @click="onClick">Click!</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
@Component({
// 全部的組件選項均可以放在這裏
})
export default class HelloWorld extends Vue {
// 初始數據能夠直接聲明爲實例的屬性
message: string = 'Welcome to Your Vue.js + TypeScript App'
// 組件方法也能夠直接聲明爲實例的方法
onClick (): void {
console.log(this.message)
console.log(_)
}
}
</script>
複製代碼
至此,運行npm run dev
npm run build
都能正常跑起來了
在 tsconfig.json
中,添加對es6 / es7
的支持,更多的配置請見tsconfig - 編譯選項
"lib": [
"dom",
"es5",
"es6",
"es7",
"es2015.promise"
]
複製代碼
在建立的 src/vue-shim.d.ts
文件中,增長以下代碼
// 聲明全局方法
declare module 'vue/types/vue' {
interface Vue {
$Message: any,
$Modal: any
}
}
複製代碼
這樣,以後再使用this.$message()的話就不會報錯了
若是咱們在項目中有使用 jquery,lodash
這樣的工具庫的時候,確定不但願在全部用到的地方都import _ from ‘lodash’
那咱們就來配置一下:
首先仍是在webpack.base.conf.js
中添加一個插件、並把這個 vendor
拉出來
entry: {
app: './src/main.ts',
vendor: [
"lodash"
]
}
plugins: [
new webpack.ProvidePlugin({
_: 'lodash'
})
]
複製代碼
上面的意思是,當模塊使用這些變量的時候wepback
會自動加載
而後,你須要告訴eslint
這個 _
是全局的
在.eslintrc.js
中添加
globals: {
_: true
},
複製代碼
接下來,你還須要告訴ts
這個 _
是全局的
在vue-shim.d.ts
declare global {
const _: typeof lodash
}
複製代碼
這樣,在其餘地方就能夠用_
直接調用啦
# 安裝依賴
npm i vuex vuex-class --save
複製代碼
vue
中集中管理應用狀態vue-class-component
寫法中 綁定 vuex
到這裏就結束了最基礎的配置啦,但願能夠幫到你噢!