一、背景

基础项目

基础项目是基于vue-cli+webpack-dev-middleware的项目。首先前端使用vue-cli搭建的环境(本教程略),然后使用express,配合webpack-dev-middleware发布前端文件。
server.js文件如下:

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./config/index')

const app = express();
const webpackConfig = require('./build/webpack.server.conf.js');
const compiler = webpack(webpackConfig);

// Tell express to use the webpack-dev-middleware and use the webpack.config.js
// configuration file as a base.
app.use(webpackDevMiddleware(compiler, {
  publicPath: webpackConfig.output.publicPath
}));

// Serve the files on port 3000.
const PORT = config.dev.port
app.listen(PORT, function () {
  console.log('Example app listening on port ' + PORT + '!\n');
});

Github项目:Vue-webpackDevMiddleware基础项目

webpack-dev-middleware

An express-style development middleware for use with webpack bundles and allows for serving of the files emitted from webpack. This should be used for development only.

仅用于开发环境,通常与Express配合使用,通过webpack工具可以对前端文件进行打包,配合webpack-hot-middleware可实现热加载。
Github项目:webpack-dev-middleware

二、开始改造

安装一些依赖

为了支持TypeScript,需要增加一些依赖:

  • style-loader 1.0.0
  • ts-loader 6.0.4
  • tslint 5.18.0
  • tslint-config-standard 8.0.1
  • tslint-loader 3.5.4
  • typescript 3.2.2
  • vue-class-component 7.1.0
  • vue-property-decorator 8.1.1

注意,安装这些依赖要指定版本,不然不同版本的插件可能会不兼容。在package.json中,也把依赖的版本^去掉,防止不兼容。
除了新增了一些依赖,同时某些依赖的版本也有变化,建议直接拷贝下面的package.json文件,然后npm install安装。

{
  "name": "Vue-webpackDevMiddleware",
  "version": "1.0.0",
  "description": "A Vue project based on express. Use Webpack-dev-middleware to build a dev enviroment.",
  "author": "David-Shi-1989 <287841605@qq.com>",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "webpack-dev-server --open",
    "server": "node server.js",
    "unit": "jest --config test/unit/jest.conf.js --coverage",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
    "build": "node build/build.js"
  },
  "dependencies": {
    "vue": "2.5.16",
    "vue-router": "3.0.1"
  },
  "devDependencies": {
    "autoprefixer": "7.2.6",
    "babel-core": "6.26.3",
    "babel-eslint": "8.2.3",
    "babel-helper-vue-jsx-merge-props": "2.0.3",
    "babel-jest": "21.2.0",
    "babel-loader": "7.1.4",
    "babel-plugin-dynamic-import-node": "1.2.0",
    "babel-plugin-syntax-jsx": "6.18.0",
    "babel-plugin-transform-es2015-modules-commonjs": "6.26.2",
    "babel-plugin-transform-runtime": "6.23.0",
    "babel-plugin-transform-vue-jsx": "3.7.0",
    "babel-preset-env": "1.6.1",
    "babel-preset-stage-2": "6.24.1",
    "babel-register": "6.26.0",
    "chalk": "2.4.1",
    "clean-webpack-plugin": "0.1.19",
    "copy-webpack-plugin": "4.5.1",
    "cross-spawn": "5.1.0",
    "css-loader": "0.28.11",
    "eslint": "4.19.1",
    "eslint-config-standard": "10.2.1",
    "eslint-friendly-formatter": "3.0.0",
    "eslint-loader": "2.1.0",
    "eslint-plugin-import": "2.11.0",
    "eslint-plugin-node": "5.2.1",
    "eslint-plugin-promise": "3.7.0",
    "eslint-plugin-standard": "3.1.0",
    "eslint-plugin-vue": "4.5.0",
    "express": "4.16.3",
    "extract-text-webpack-plugin": "4.0.0-beta.0",
    "file-loader": "1.1.11",
    "font-awesome": "4.7.0",
    "friendly-errors-webpack-plugin": "1.7.0",
    "html-webpack-plugin": "3.2.0",
    "jest": "22.4.3",
    "jest-serializer-vue": "0.3.0",
    "nightwatch": "0.9.21",
    "node-notifier": "5.2.1",
    "optimize-css-assets-webpack-plugin": "3.2.0",
    "ora": "1.4.0",
    "portfinder": "1.0.13",
    "postcss-import": "11.1.0",
    "postcss-loader": "2.1.4",
    "postcss-url": "7.3.2",
    "rimraf": "2.6.2",
    "selenium-server": "3.11.0",
    "semver": "5.5.0",
    "shelljs": "0.7.8",
    "style-loader": "1.0.0",
    "ts-loader": "6.0.4",
    "tslint": "5.18.0",
    "tslint-config-standard": "8.0.1",
    "tslint-loader": "3.5.4",
    "typescript": "3.2.2",
    "uglifyjs-webpack-plugin": "1.2.5",
    "url-loader": "0.5.9",
    "vue-class-component": "7.1.0",
    "vue-jest": "1.4.0",
    "vue-loader": "14.2.2",
    "vue-property-decorator": "8.1.1",
    "vue-style-loader": "3.1.2",
    "vue-template-compiler": "2.5.16",
    "webpack": "4.0.0",
    "webpack-bundle-analyzer": "2.11.1",
    "webpack-cli": "2.1.2",
    "webpack-dev-middleware": "2.0.6",
    "webpack-dev-server": "3.1.0",
    "webpack-merge": "4.1.2"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

修改webpack配置文件

与vue-cli搭出来的纯前端不同,我们这里使用express发布,webpack配置文件在build\webpack.server.conf.js。需要修改以下几点:

1. entry入口文件
入口文件由main.js改为main.ts

entry: {
  app: './src/main.ts'
}

同时把src\main.js重命名为main.ts,文件内容如下:

// 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/index'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  router,
  render: (h) => h(App)
}).$mount('#app')

2. resolve.extensions增加ts

resolve: {
  extensions: ['.ts', '.js', '.vue', '.json'],
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('./src'),
  }
}

3. module.rules增加ts的规则

{
  test: /\.ts$/,
  exclude: /node_modules/,
  enforce: 'pre',
  loader: 'tslint-loader'
},
{
  test: /\.tsx?$/,
  loader: 'ts-loader',
  exclude: /node_modules/,
  options: {
    appendTsSuffixTo: [/\.vue$/],
  }
}

4. 新建文件tsconfig.json

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "allowJs": true,
    "module": "esnext",
    "target": "es5",
    "moduleResolution": "node",
    "isolatedModules": true,
    "lib": [
      "dom",
      "es5",
      "es2015.promise"
    ],
    "sourceMap": true,
    "pretty": true
  }
}

5. 新建文件src\vue-shims.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

6. 新建文件tslint.json

{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
}

此时到这里,运行server.js(node server.js),页面可以正常打开。

修改vue文件

此时项目已经支持ts了,接下来修改vue文件,将js代码改为ts代码。

1. 修改App.vue
由于实现等原因,ts代码放进vue文件不生效,所以把ts代码提出来,放在单独的ts文件里,在vue文件中引入。
App.vue:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script lang="ts" src="./App.ts"></script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

App.ts:

import Vue from 'vue'
import { Component } from 'vue-property-decorator'

@Component({
})
export default class App extends Vue{
  created () {
    console.log(`App created`)
  }
}

2. 新增组件person-info.vue
person-info.vue:

<template>
  <p>
    <span>{{name}}</span>
    <span>{{age}}</span>
    <span>{{nickName}}</span>
    <span>{{timestamp}}</span>
  </p>
</template>

<script lang="ts" src="./person-info.ts">
</script>
<style scoped>
</style>

person-info.ts:

import { Vue, Component, Prop } from 'vue-property-decorator'

@Component({})
export default class HelloWorld extends Vue {
  @Prop(String) name: string | undefined
  @Prop(Number) age: number | undefined
  @Prop(String) nickName: string | undefined
  @Prop(String) timestamp: string
}

3. 修改HelloWorld.vue
HelloWorld.vue引入person-info组件:

<template>
    <div class="hello">
      <h1>{{ msg }}</h1>
      <h2>Essential Links</h2>
    <ul>
      <li>
        <a
          href="https://vuejs.org"
          target="_blank"
        >
          Core Docs
        </a>
      </li>
      <li>
        <a
          href="https://forum.vuejs.org"
          target="_blank"
        >
          Forum
        </a>
      </li>
      <li>
        <a
          href="https://chat.vuejs.org"
          target="_blank"
        >
          Community Chat
        </a>
      </li>
      <li>
        <a
          href="https://twitter.com/vuejs"
          target="_blank"
        >
          Twitter
        </a>
      </li>
      <br>
      <li>
        <a
          href="http://vuejs-templates.github.io/webpack/"
          target="_blank"
        >
          Docs for This Template
        </a>
      </li>
    </ul>
    <h2>Ecosystem</h2>
    <ul>
      <li>
        <a
          href="http://router.vuejs.org/"
          target="_blank"
        >
          vue-router
        </a>
      </li>
      <li>
        <a
          href="http://vuex.vuejs.org/"
          target="_blank"
        >
          vuex
        </a>
      </li>
      <li>
        <a
          href="http://vue-loader.vuejs.org/"
          target="_blank"
        >
          vue-loader
        </a>
      </li>
      <li>
        <a
          href="https://github.com/vuejs/awesome-vue"
          target="_blank"
        >
          awesome-vue
        </a>
      </li>
    </ul>
    <personInfo :name="name" :age="age" nickName="sdas" :timestamp="ts"></personInfo>
  </div>
</template>

<script lang="ts" src="./HelloWorld.ts"></script>

<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

HelloWorld.ts文件:


import { Vue, Component } from 'vue-property-decorator'
import personInfo from './person-info.vue'

@Component({
  components: {
    personInfo
  }
})
export default class HelloWorld extends Vue {
  msg: string = 'Welcome Typescript + Vue'
  ts: string = ''
  name: string = 'david'
  age: number = 29
  created () {
    this.ts = String(Date.now().valueOf())
    console.log(this.ts)
  }
}

三、结束

运行效果

此时,运行出来的页面如下:
最终效果

总结

1. 改进

  • 解决告警mode
    WARNING in configuration. The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.
    解决办法:在build\webpack.server.conf.js中增加mode如下:

    // ...
    module.exports = {
      entry: {
        app: './src/main.ts'
      },
      mode: 'development',
      // ...
  • 告警
    Warning: The 'await-promise' rule requires type information.等告警
    解决办法: 待解决
  • ts代码写入vue文件中
    解决办法: 待解决

davidshi
33 声望6 粉丝

Deliver Happiness