webpack中 vue + ts + jsx應該怎麼配置?
現在配置了vue + typescript但是如何在class風格組件中使用jsx呢?
折騰不一定能提高生產力,但是不折騰一定不能提高生產力。—— 魯迅
瀉藥。
TS的星星之火現在已經可以燎原了,TS + React、TS + Angular已經普及在很多新項目中。Vue似乎稍稍落後了一小步,不過目前的生態也還不錯了。
不知道題主目前處於什麼狀態,因此就從頭寫起好了~
Class風格
在ES6、ES7中,我們可以使用Class、Decrator等新特性讓我們的代碼更便於書寫。在TS中,這些寫法的優勢尤為明顯,因此在接入TS之前,我們需要先掌握下面幾個最佳實踐:
- vue-class-component
- vuex-class
- vue-property-decorator
伸手黨福利
如果你想快速開始一個新的TS項目,可以使用餓了么提供的TS模板。
vue init ElemeFE/webpack-typescript ts-vue
DIY
初始化
如果你想一步步實現自己的個性化配置,可以從這裡看。
這裡我使用的是webpack模板建立項目:
vue init webpack ts-vue
// 我的依賴
Project name ts-vue
Project description A Vue.js project
Author geovanni.zhang
Vue build standalone
Install vue-router? Yes
Use ESLint to lint your code? Yes
Set up unit tests Yes
Pick a test runner jest
Setup e2e tests with Nightwatch? Yes
安裝依賴
npm install
npm install typescript ts-loader --save-dev
配置TS解析
在build/webpack.base.con.js中添加規則,使webpack能夠解析.vue文件。同時在resolve中添加ts後綴,使webpack中能夠解析.ts文件:
module.exports = {
...
resolve: {
extensions: [".js", ".ts", ".vue", ".json"],
alias: {
"vue$": "vue/dist/vue.esm.js",
"@": resolve("src"),
}
},
module: {
rules: [
{
// 對所有引入的tsx文件進行解析
test: /.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/,
options: {
// 自動將所有.vue文件轉化為.vue.tsx文件
appendTsSuffixTo: [/.vue$/]
}
}
]
}
...
}
為每個文件聲明全局模塊.vue,這樣每個單文件組件就可以被自動被聲明了。
在src目錄下建立一個文件vue.d.ts:
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
在根目錄下建立配置文件tsconfig.json
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"sourceMap": true,
"strict": true,
"module": "es2015",
"moduleResolution": "node",
"target": "es5"
}
}
重命名所有JS文件為TS文件。
如果這是一個新的項目應該只有src/main.js和src/router/index.js。
-- src
-- main.js
-- router
-- index.js
回到build/webpack.base.conf.js文件中,修改入口文件的後綴:
module.exports = {
entry: {
app: "./src/main.ts"
}
}
在所有文件中,引入.vue文件時,.vue不能省略。
這是因為我們實際引用的是App.vue.tsx,tsx已經被省略了。在這裡修改src/main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue"
import App from "./App.vue"
import router from "./router"
Vue.config.productionTip = false
/* eslint-disable no-new */ 修改src下的所有vue文件,將script的語言設置為ts,並添加聲明: // App.vue 大功告成,可以運行啦。
new Vue({
el: "#app",
router,
template: "&
components: { App }
})
// HelloWorld.vue
&
&
更舒適的寫法
class風格
我們可以使用vue-class-component編寫class風格的組件,讓代碼更有調理。首先需要安裝依賴包:
npm install --save-dev vue-class-component
修改tsconfig.json,添加裝飾器模式:
{
"compilerOptions": {
"jsx": "preserve",
"jsxFactory": "h",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true, // 主要是這一行
"sourceMap": true,
"module": "es2015",
"moduleResolution": "node",
"target": "es5"
}
}
現在我們可以使用class風格進行編寫了,修改HelloWorld組件:
&
裝飾器風格
如果你覺得上面的方法還不夠簡潔和明了,可以嘗試使用下面的這種風格。像python一樣使用裝飾器來定義不同屬性。
首先需要安裝依賴包:
npm install --save-dev vue-property-decorator
在組件中引入並使用裝飾器:
&
我們還能夠使用以下裝飾器:
@Component
export class MyComponent extends Vue {
@Emit()
addToCount(n: number){ this.count += n }
@Emit("reset")
resetCount(){ this.count = 0 }
@Inject() foo: string
@Inject("bar") bar: string
@Inject(s) baz: string
@Model("change") checked: boolean
@Prop()
propA: number
@Prop({ default: "default value" })
propB: string
@Prop([String, Boolean])
propC: string | boolean
@Provide() foo = "foo"
@Provide("bar") baz = "bar"
@Watch("child")
onChildChanged(val: string, oldVal: string) { }
@Watch("person", { immediate: true, deep: true })
onPersonChanged(val: Person, oldVal: Person) { }
}
引入JSX
題主的問題主要看這裡解決~
Vue支持JSX
首先我們要安裝解析JSX的依賴,主要是通過babel解決:
npm install
babel-plugin-syntax-jsx
babel-plugin-transform-vue-jsx
babel-helper-vue-jsx-merge-props
babel-preset-es2015
--save-dev
接的在.babelrc文件中註冊插件:
{
...
"plugins": ["transform-runtime", "transform-vue-jsx"],
...
}
我們來修改一下原來的組件,將template換為JSX:
// 這裡我們設置為tsx
&
TS解析Vue
在build/webpack.base.conf.js中修改規則,使webpack識別tsx代碼的時候先用babel解析一下JSX。同時在resolve中添加tsx後綴,使webpack能夠解析tsx代碼:
module.exports = {
...
resolve: {
extensions: [".js", ".ts", ".vue", ".tsx", ".json"],
alias: {
"vue$": "vue/dist/vue.esm.js",
"@": resolve("src"),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /.vue$/,
loader: "vue-loader",
options: Object.assign(vueLoaderConfig, {
loaders: {
ts: "ts-loader",
tsx: "babel-loader!ts-loader"
}
})
},
{
test: /.tsx?$/,
exclude: /node_modules/,
use: [
"babel-loader",
{
loader: "ts-loader",
options: { appendTsxSuffixTo: [/.vue$/] }
}
]
}
]
}
...
}
不要忘記在tsconfig.json中添加JSX的支持:
{
"compilerOptions": {
"jsx": "preserve",
"jsxFactory": "h",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"sourceMap": true,
"module": "es2015",
"moduleResolution": "node",
"target": "es5"
}
}
這裡記得去掉嚴格模式
其他
Vuex
Github上有一個比較好的Vuex實踐,這裡就不再贅述了。
TSLint
對於js代碼我們有ESLint,對於ts代碼也可以引入TSLint。
首先安裝需要的依賴包:
npm install --save-dev tslint tslint-loader tslint-config-standard
修改build/webpack.base.conf.js中的匹配規則,對於ts和tsx文件在編譯前先進行校驗:
module: {
rules: [
...
{
test: /.tsx?$/,
exclude: /node_modules/,
enforce: "pre",
loader: "tslint-loader"
}
...
]
...
}
在build/vue-loader.conf.js中,添加開啟TSLint進行預處理:
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ["src", "poster"],
source: "src",
img: "src",
image: "xlink:href"
},
ts: ["ts-loader", "tslint-loader"]
}
在根目錄下添加TSLint的配置文件,這裡我是這樣配置的:
{
"extends": "tslint-config-standard",
"globals": {
"require": true
},
"rules": {
"no-consecutive-blank-lines": false
}
}
重新運行,就可以看到warning啦~
結束
閑逛的時候發現有個同學已經寫了demo,這裡放個鏈接,向先人star~
以上~
我有一個repo,同時有lang="ts"和lang="tsx"兩種vue,以及純tsx和ts文件,正在整理總結,晚點放出
repo地址: https://github.com/Plasmatium/amazing-canvas-collection
網上有一些其他的教程,提到了各種需要安裝的包和一些操作,實現了部分typescript、jsx、單文件vue的組合,比如單獨的tsx(通過import導入template和css),單文件vue啟用&