Browse Source

Docsify Auto Published

willin 8 years ago
parent
commit
6b48037c73
2 changed files with 270 additions and 0 deletions
  1. 1 0
      _sidebar.md
  2. 269 0
      experience/advanced/vue-webapp.md

+ 1 - 0
_sidebar.md

@@ -84,6 +84,7 @@
     - [Electron桌面应用](experience/advanced/desktop-app.md)
     - [Electron桌面应用](experience/advanced/desktop-app.md)
     - [混合Web应用实践](experience/advanced/mixed-project.md)
     - [混合Web应用实践](experience/advanced/mixed-project.md)
     - [基于接口快速搭建前后分离项目](experience/advanced/webapp-proxy.md)
     - [基于接口快速搭建前后分离项目](experience/advanced/webapp-proxy.md)
+    - [Vue + Koa前后端分离实践](experience/advanced/vue-webapp.md)
 - 思想篇
 - 思想篇
   - 能力
   - 能力
     - [好员工的定义](mind/capability/define-good-employee.md)
     - [好员工的定义](mind/capability/define-good-employee.md)

+ 269 - 0
experience/advanced/vue-webapp.md

@@ -0,0 +1,269 @@
+# Vue + Koa 前后端分离实践
+
+# 配置
+
+## Webpack
+
+`vue-cli`及诸多脚手架生成的项目里, 配置项非常繁琐, 结构也非常混乱, 实际上webpack常规配置就需要两个, 分别给开发环境和产品环境使用.
+
+而且像 `webpack-merge` 这样的插件, 可以通过简单的 `Object.assign` 或 `[].concat` 完成.
+
+示例:
+
+`base.js` 基础设置:
+
+```js
+
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+
+module.exports = {
+  entry: path.resolve(__dirname, '../src/main.js'),
+  output: {
+    path: path.resolve(__dirname, '../dist'),
+    publicPath: '/',
+    filename: '[name].[hash].js',
+    chunkFilename: '[id].[hash].js'
+  },
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: {
+          extractCSS: true
+        }
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        exclude: /node_modules/
+      },
+      {
+        test: /\.(png|jpg|gif|svg)$/,
+        loader: 'file-loader',
+        options: {
+          name: '[name].[ext]?[hash]'
+        }
+      }
+    ]
+  },
+  resolve: {
+    extensions: ['.js', '.json', '.vue'],
+    alias: {
+      vue$: 'vue/dist/vue.esm.js'
+    }
+  },
+  performance: {
+    hints: false
+  },
+  plugins: [
+    new ExtractTextPlugin('style.css'),
+    new HtmlWebpackPlugin({
+      filename: path.resolve(__dirname, '../dist/index.html'),
+      template: path.resolve(__dirname, '../index.html'),
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    })
+  ]
+};
+``` 
+
+dev 配置:
+
+```js
+const base = require('./base');
+
+module.exports = Object.assign({}, base, {
+  devtool: '#eval-source-map',
+  devServer: {
+    historyApiFallback: true,
+    noInfo: true
+  }
+});
+```
+
+prod 配置:
+
+```js
+
+const webpack = require('webpack');
+const base = require('./base');
+
+module.exports = Object.assign({}, base, {
+  devtool: '#source-map',
+  devServer: {
+    historyApiFallback: true,
+    noInfo: true
+  },
+  plugins: (base.plugins || []).concat([
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: '"production"'
+      }
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      sourceMap: true,
+      compress: {
+        warnings: false
+      }
+    }),
+    new webpack.LoaderOptionsPlugin({
+      minimize: true
+    })
+  ])
+});
+```
+
+## Babel
+
+`preset-latest` 或 `preset-2015` 之类的东西, 谨慎添加. 慢慢必要性也不会太大.
+
+```js
+module.exports = {
+  presets: [
+     ['env', { modules: false }]
+  ],
+  plugins: ['transform-runtime'],
+  comments: false
+};
+```
+
+## ESLint
+
+这里是我用的配置:
+
+```js
+module.exports = {
+  root: true,
+  env: {
+    browser: true,
+    es6: true,
+    node: true
+  },
+  extends: [
+    'dwing'
+  ],
+  plugins: [
+    'html',
+    'vue'
+  ],
+  rules: {
+    'no-new': 0,
+    'no-bitwise': 0,
+    'import/extensions': ['error', 'always', { 'js': 'never', 'vue': 'never' }],
+    'import/no-extraneous-dependencies': 0
+  },
+  settings: {
+    'import/resolver': {
+      webpack: {
+        config: './config/base.js'
+      }
+    }
+  }
+};
+```
+
+在 vscode 下默认是无法对 `.vue` 文件进行 autofix 的.
+
+需要注意其中的两个插件, 一个是`eslint-plugin-html`, 一个是`eslint-plugin-vue`, 同时要修改 vscode 的配置 `eslint.validate`, 参考:
+
+```js
+// 将设置放入此文件中以覆盖默认设置
+{
+  "editor.tabSize": 2,
+  "[vue]": {
+    "editor.formatOnSave": true
+  },
+  "eslint.autoFixOnSave": true,
+  "eslint.validate": [
+    "javascript",
+    "javascriptreact",
+     { "language": "vue", "autoFix": true },
+     { "language": "html", "autoFix": true }
+  ]
+}
+```
+
+# 后端渲染
+
+根据项目来权衡,是否需要进行服务器端渲染(SSR).
+
+本项目中采用前后端完全分离的做法, 后端将直接透传前端相关的请求. 目前市面上大多数 devServer 都是用 express 框架做的,而实际项目中用到 express 的可能性小之又小. 找了很久 koa 相关的,都无法跑通,这里我就自己搞了一个能够在 koa 上进行开发运行的方法.
+
+## 开发环境
+
+使用 `Stream PassThrough` 将请求结果转发到前端 `webpack-dev-server`
+
+```js
+const { PassThrough } = require('stream');
+
+router.get('/', (ctx) => {
+  ctx.set('Content-Type', 'text/html');
+  // webpack-dev-server 端口 9000
+  ctx.body = request.get('http://localhost:9000/index.html').pipe(PassThrough());
+});
+
+router.get('/(.*)', async (ctx) => {
+  const path = ctx.path.split('.').reverse();
+  if (path.length > 0) {
+    const type = path[0];
+    switch (type) {
+      case 'css': {
+        ctx.set('Content-Type', 'text/css');
+        break;
+      }
+      case 'js': {
+        ctx.set('Content-Type', 'text/javascript');
+        break;
+      }
+      case 'jpg': {
+        ctx.set('Content-Type', 'image/jpeg');
+        break;
+      }
+      case 'png': {
+        ctx.set('Content-Type', 'image/png');
+        break;
+      }
+      default: {
+        ctx.set('Content-Type', 'text/plain');
+      }
+    }
+  }
+  ctx.body = request.get(`http://localhost:9000${ctx.path}`).pipe(PassThrough());
+});
+```
+
+唯一的不足就是, `PassThough` 默认的 mime 是 `application/octet-stream` 需要手动替换头信息.
+
+## 产品环境
+
+`koa-send` 就可以满足:
+
+```js
+const send = require('koa-send');
+
+router.get('/(.*)', async (ctx) => {
+  try {
+    await send(ctx, '/index.html', { root: path.resolve(__dirname, '../dist') });
+  } catch (e) { }
+});
+router.get('/(.*)', async (ctx) => {
+  try {
+    await send(ctx, ctx.path, { root: path.resolve(__dirname, '../dist') });
+  } catch (e) { }
+});
+```
+
+---
+
+项目源码: <https://github.com/willin/koa-api-logger-ui>