koa+mysql+vue+socket.io全栈开发之前端篇

原文地址:koa+mysql+vue+socket.io全栈开发之前端篇

ReactVue 之间的对比,是前端的一大热门话题。

  • vue 简易上手的脚手架,以及官方提供必备的基础组件,比如 vuexvue-router,对新手真的比较友好;react 则把这些都交给社区去做,虽然这壮大了 react 的生态链,但新手要弄出一套趁手的方案挺麻烦的,不过好在现在有很多类似 dva的方案了。

  • vue 比较讨喜的一点,就是它的数据双向流动在表单开发时特别方便,而 react 在这方面可就麻烦多了。

  • 但是 vue 复杂的 api ,简直让人头大,光是文档说明都几十页了。太多的语法,太多的魔法符号,对进化速度越来越快的前端届来说,就是入手这个框架的最大阻碍。

  • 而相反 react 的 api 数量简直可以忽略不计了,顶多花几小时就能看完官方文档。你只要理解 JavaScript,就能理解 react 的很多行为。react 的很多用法,它的 api 都是符合直觉的,你对它用法的猜测基本都是八九不离十的,这真是大大降低了心智负担。

  • 除此之外,reactjsx 语法表达能力更强,还有 hochooks 使代码也更容易组织和复用。

虽然我更喜欢 React ,但工作上的需求,还不是要你用什么你就得用什么,所以这个 demo 就当是探索 Vue 的前奏。

之前我还是有用过 vue 的,记得还是 1.0 版本,当时的潮流就是类似 angular 1.x 的 mvvm 方案,数据双向流动。那时的 vue 远没有现在的热度,组件也少,没有 vue-router,没有 vuex,组件之前的通信简直太痛苦了。现在 vue 2.x 比起之前,已经发生了天翻地覆的变化,vue 也在不断向 react 靠拢,而我也只能从头开始学起。

闲话说得有点多,还是赶紧进入主题吧

项目配置

选择 webpack 4 打包和管理,template 引擎使用 pug ,css 预编译是 scss。

webpack.common.js 的配置

// webpack.common.js
module.exports = {
    entry: './src/main.js',
    output: {
        path: resolve(__dirname, 'dist'),
        filename: '[name]-[hash].js'//输出文件添加hash
    },
    optimization: { // 代替commonchunk, 代码分割
        runtimeChunk: 'single',
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
    },
    module: {
        rules: [
            {
                test:/\.vue$/,
                exclude: /node_modules/,
                use:['vue-loader']
            },
            {
                test: /\.js?$/,
                exclude: /node_modules/,
                use: ['babel-loader']//'eslint-loader'
            },
            {
                test: /\.pug$/,
                use: ['pug-plain-loader']
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.scss$/,
                use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
            },
            {   
                test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 1000
                    }
                }]
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new CleanWebpackPlugin([resolve(__dirname, 'dist')]),//生成新文件时,清空生出目录
        new HtmlWebpackPlugin({
            template: './public/index.html',//模版路径
            filename: 'index.html',//生成后的文件名,默认index.html
            favicon: './public/favicon.ico',
            minify: {
                removeAttributeQuotes:true,
                removeComments: true,
                collapseWhitespace: true,
                removeScriptTypeAttributes:true,
                removeStyleLinkTypeAttributes:true
             }
        }),
        new HotModuleReplacementPlugin()//HMR
    ]
};

webpack.dev.js 的配置

就是开发服务器 devServer的配置,监控代码变更。

// webpack.dev.js
module.exports = merge(common, {
    mode: 'development',
    devtool: 'inline-source-map',
    devServer: {
        contentBase: './dist',
        index:'index.html',
        port: 3002,
        compress: true,
        historyApiFallback: true,
        hot: true
    }
});

babel.config.js 的配置

module.exports = {
  presets: [
    [
      '@vue/app', {
        "useBuiltIns": "entry"
      }
    ]
  ]
}

目录结构

public #公共目录
server #后端目录
src    #前端目录
├── assets #静态文件目录
├── common #工具目录
├── components #组件目录
├── store   # vuex store目录
├── App.vue # 根组件
├── main.js # 入口文件
└── router.js #路由    

入口和路由

路由文件

下面使用了嵌套路由,使用的是基于 history 的路由,也可以选择基于 hashchange的路由。

import Vue from 'vue'
import Router from 'vue-router'
//...

Vue.use(Router)

//路由
const routes = [{
    path: '/',
    name: 'home',
    component: Index
},{
    path: '/sign',
    name: 'sign',
    component: Sign,
    children: [ //嵌套路由
        {
            path: "log",
            name: "login",
            component: Login
        },
        {
            path: "reg",
            name: "register",
            component: Register
        },
        { path: '*', redirect: 'log' }
    ]
}, { path: '*', redirect: '/' }]

export default new Router({
    mode: "history",
    routes
})

入口文件

把router,store 和根组件组合起来

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '../public/base.min.css'
import '../public/fontello.css'

Vue.config.productionTip = false
new Vue({
    router,
    store,
    render: h => h(App),
}).$mount('#app')

模块的编写

模版,逻辑代码,样式合成到一个页面也是我欣赏 vue 的一个方面,因为这样你就不需要在多个文件之间反复的切换。

模版template

pug 就是之前的 jade,它的简洁在复杂的页面下会让 template 清晰不少,最起码会让你少敲代码,这里以index 页面的部分代码为例。

动态创建组件

上面用到了 vue 的 动态创建组件 的概念,什么意思呢?这个组件在当前页面中是不存在的,需要我们触发之后,才开始创建。比如,当你点击某个按钮,才开始加载创建组件,然后填充到页面中来。下面就是动态组件相关功能的编写。

data() {
    return {
       wins: [] //组件列表
    }
},
methods: {  
  addWin(info, com) { // 添加组件的方法
      this.wins.push({
          msgs: info.msgs || [],
          info,
          sty: {
              left: l * 30 + 270,
              top: l * 30 + 30,
              z: 0
          },
          component: com
      });
  }
}  

//填充组件
component(:is="item.component"  v-for="(item,i) in wins" :key="item.id" 
  :info="item.info"
  :sty="item.sty"
  :msgs="item.msgs"
  v-on:close="closeWin(i)"
  v-on:setZ="setZ(i)")

javascript部分

这里就是业务逻辑的部分了,以部分代码为例, 具体的部分参考官方的文档

style部分

使用了 vue 默认的 scoped ,当然最完善的方案是 css-module,配置要复杂一些,当然这要看你项目需求。预编译器使用的是 scss,个人认为比较强大和方便。