简介
JavaScript 作为一种弱类型的语言,类型推断只能提供很有限的支持,TypeScript 提供了一种描述对象形状的方法。可以帮助提供更好的文档,还可以验证你的代码可以正常工作,在一些大型的项目中,使用 TypeScript 非常必要,从代码层次就已经避免了很多错误,而且方便文档的书写,最主要的就是后期迭代特别爽,但是对于没有接触过强类型语言(Java、C)的童鞋来说,TypeScript 上手还是有点困难了,单就目前前端发展趋势来说,TypeScript 还是很重要的,想了解更多 TypeScript 的童鞋可以自己去看 官网。
开始
我们需要利用 webpack 创建一个简单的 vue 项目,为了方便,我就直接拿上一篇文章的 Demo 了,小伙伴可以直接 clone 下来并安装依赖:
git clone https://gitee.com/vv_bug/vue-dist-demo.git && npm install
从 0 开始搭建一个 vue 项目也是非常简单的,强烈推荐大家去看我的另外一篇文章 来和 webpack 谈场恋爱吧。
然后我们试着运行一下项目:
npm run dev
然后打开浏览器看效果:
支持 TypeScript
这里我们提供两种方案,也是目前比较推荐的两种。
方式一
利用 Babel
的 @babel/plugin-transform-typescript
插件来实现。
安装 @babel/core
babel 核心 API。
npm install -D @babel/core --registry https://registry.npm.taobao.org
安装 @babel/plugin-transform-typescript 插件
Babel 的 TypeScript 插件。
npm install -D @babel/plugin-transform-typescript --registry https://registry.npm.taobao.org
安装 babel-loader
babel 加载器。
npm install -D babel-loader --registry https://registry.npm.taobao.org
ok!安装完 Babel 的一些依赖后,我们开始配置 webpack。
我们在工程目录 vue-dist-demo
下创建一个文件 babel.config.js
:
touch babel.config.js
然后写入以下代码到 babel.config.js
文件:
module.exports = {
presets: [
[
'@babel/preset-env', // 添加 preset-env 预设做语法转换跟 polyfill 添加
{
corejs: 3,
useBuiltIns: 'usage',
modules: false,
},
],
],
plugins: [
[
'@babel/plugin-transform-runtime', // 利用 runtime 做 helpers 跟 regenerator 设置
{
corejs: false,
helpers: true,
useESModules: false,
regenerator: true,
absoluteRuntime: './node_modules',
},
],
],
};
找到 webpack 的配置文件 webpack.config.js
文件,然后添加 babel-loader
:
...
.module
.rule('babel')
.test(/\.t?j?s?$/) // 对 js、ts 文件进行 babel 配置
.use('babel-loader')
.loader('babel-loader')
.end()
.end()
...
webpack.config.js
文件全部配置:
const path = require('path');
const config = new (require('webpack-chain'))();
config
.context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
.entry('app') // 入口文件名称为 app
.add('./src/main.js') // 入口文件为 ./src/main.ts
.end()
.output
.path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
.filename('[name].[hash:8].js') // 打包出来的 bundle 名称为 "[name].[hash:8].js"
.publicPath('/') // publicpath 配置为 "/"
.end()
.resolve
.extensions
.add('.js')
.add('.vue') // 配置以 .js 等结尾的文件当模块使用的时候都可以省略后缀
.end()
.end()
.module
.rule('babel')
.test(/\.t?j?s?$/) // 对 js、ts 文件进行 babel 配置
.use('babel-loader')
.loader('babel-loader')
.end()
.end()
.rule('vue') // vue-loader 相关配置
.test(/\.vue$/) // 匹配 .vue 文件
.use('vue-loader')
.loader('vue-loader')
.end()
.end()
.end()
.plugin('vue-loader-plugin') // vue-loader 必须要添加 vue-loader-plugin
.use(require('vue-loader').VueLoaderPlugin, [])
.end()
.plugin('html') // 添加 html-webpack-plugin 插件
.use(require('html-webpack-plugin'), [
{
template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
chunks: ['app'], // 指定需要加载的 chunk
inject: 'body', // 指定 script 脚本注入的位置为 body
},
])
.end()
.devServer
.host('0.0.0.0') // 服务器外部可访问
.disableHostCheck(true) // 关闭白名单校验
.contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
.historyApiFallback({
disableDotRule: true, // 禁止在链接中使用 "." 符号
rewrites: [
{ from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
],
})
.port(8080) // 当前端口号
.hot(true) // 打开页面热载功能
.sockPort('location') // 设置成平台自己的端口
.open(true);
module.exports = config.toConfig();
ok!就是这么简单,接着我们来测试一下。
测试
我们修改一下 src/app.vue
:
<template>
<div class="app">{{ msg }}</div>
</template>
<script lang="ts">
export default {
name: "app",
data(){
return {
msg: "hello"
}
},
created() {
let name: string = "hello ts";
alert(name);
}
}
</script>
可以看到,我们给 script
标签加了一个 lang="ts"
属性,并且在 create 方法中添加了一段 TypeScript 代码,然后我们重新运行看一下效果:
npm run dev
可以看到,webpack 正常编译了我们的 ts 语法,项目正常运行。
方式二
利用 ts-loader
结合官方 typescript
库来实现。
安装 typescript
ts
语法核心 API。
npm install -D typescript --registry https://registry.npm.taobao.org
安装 ts-loader
ts
文件加载器。
npm install -D ts-loader --registry https://registry.npm.taobao.org
除了配置 ts-loader
外,我们还需要在工程目录 vue-dist-demo
下创建一个 ts
配置文件 tsconfig.json
:
touch tsconfig.json
然后写入以下代码到 tsconfig.json
文件:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"suppressImplicitAnyIndexErrors": true,
"resolveJsonModule": true,
"sourceMap": true,
"baseUrl": ".",
"types": ["webpack-env"],
"paths": {
"@/*": ["src/*"]
},
"lib": ["esnext", "dom", "dom.iterable"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
然后找到 webpack 配置文件 webpack.config.js
,添加 ts-loader
:
...
.module
.rule("type-script") // 配置 ts-loader
.test(/\.tsx?$/) // loader 加载的条件是 ts 或 tsx 后缀的文件
.use("ts-loader")
.loader("ts-loader")
.options({ // ts-loader 相关配置
transpileOnly: true, // 只用于编译
appendTsSuffixTo: ['\\.vue$'] // 给 .vue 文件添加后缀
})
.end()
.end()
...
webpack.config.js
文件全部配置:
const path = require('path');
const config = new (require('webpack-chain'))();
config
.context(path.resolve(__dirname, '.')) // webpack 上下文目录为项目根目录
.entry('app') // 入口文件名称为 app
.add('./src/main.ts') // 入口文件为 ./src/main.ts
.end()
.output
.path(path.join(__dirname, './dist')) // webpack 输出的目录为根目录的 dist 目录
.filename('[name].[hash:8].js') // 打包出来的 bundle 名称为 "[name].[hash:8].js"
.publicPath('/') // publicpath 配置为 "/"
.end()
.resolve
.extensions
.add('.js')
.add('.vue') // 配置以 .js 等结尾的文件当模块使用的时候都可以省略后缀
.end()
.end()
.module
.rule("type-script") // 配置 ts-loader
.test(/\.tsx?$/) // loader 加载的条件是 ts 或 tsx 后缀的文件
.use("ts-loader")
.loader("ts-loader")
.options({ // ts-loader 相关配置
transpileOnly: true, // 只用于编译
appendTsSuffixTo: ['\\.vue$'] // 给 .vue 文件添加后缀
})
.end()
.end()
.rule('vue') // vue-loader 相关配置
.test(/\.vue$/) // 匹配 .vue 文件
.use('vue-loader')
.loader('vue-loader')
.end()
.end()
.end()
.plugin('vue-loader-plugin') // vue-loader 必须要添加 vue-loader-plugin
.use(require('vue-loader').VueLoaderPlugin, [])
.end()
.plugin('html') // 添加 html-webpack-plugin 插件
.use(require('html-webpack-plugin'), [
{
template: path.resolve(__dirname, './public/index.html'), // 指定模版文件
chunks: ['app'], // 指定需要加载的 chunk
inject: 'body', // 指定 script 脚本注入的位置为 body
},
])
.end()
.devServer
.host('0.0.0.0') // 服务器外部可访问
.disableHostCheck(true) // 关闭白名单校验
.contentBase(path.resolve(__dirname, './public')) // 设置一个 express 静态目录
.historyApiFallback({
disableDotRule: true, // 禁止在链接中使用 "." 符号
rewrites: [
{ from: /^\/$/, to: '/index.html' }, // 将所有的 404 响应重定向到 index.html 页面
],
})
.port(8080) // 当前端口号
.hot(true) // 打开页面热载功能
.sockPort('location') // 设置成平台自己的端口
.open(true);
module.exports = config.toConfig();
测试
重命名 src/main.js
为 src/main.ts
,然后跟方式一一样,修改一下 src/app.vue
文件:
<template>
<div class="app">{{ msg }}</div>
</template>
<script lang="ts">
export default {
name: "app",
data(){
return {
msg: "hello"
}
},
created() {
let name: string = "hello ts-loader";
alert(name);
}
}
</script>
然后我们重新运行看一下效果:
npm run dev
可以看到,webpack 正常编译了我们的 ts 语法,项目正常运行。
总结
“方式一” 还是有局限性的,只是单纯的去转换 ts 语法,在平时项目开发中还是推荐使用 “方式二”,因为更符合 TypeScript 官网规范,支持的功能也更丰富,而且通过设置 tsconfig.json
配置文件简单清晰明了,并且能够与 IDE 完美结合,实现 ts 语法智能提示功能。