65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 Vue & Webpack 的说明

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2017 年 12 月 27 日

CPOL

6分钟阅读

viewsIcon

11928

downloadIcon

133

这是一篇关于 Vue & Webpack 的笔记。

引言

这是一篇关于 Vue & Webpack 的笔记。

背景

AngularReactVueCommonJSTypescriptES5ES6 (ES2015) 和 BabelSystemJSWebpack。 有太多的术语需要学习,有太多的术语做着相同的事情,而且还相当固执己见。 在 Angular、React 和 Vue 中,Vue 鲜为人知,但拥有最好的文档,并且至少努力做到不那么固执己见,而且正在迅速 gaining its popularity(获得普及)。

  • Angular - Angular 是重量级的。“zone.js” 变更检测方法使默认行为全局化。它也使得性能优化非常复杂和繁琐
  • React - React 更轻量级,而且不那么固执己见。“setState()” 函数减轻了变更检测的繁重工作。但如果状态更改发生在多个组件中,则需要额外的工作;
  • Vue - Vue 更轻量级,而且不那么固执己见。“Object.defineProperties()” 变更检测方法精确地指出了已更改的内容。它结合了 Angular 和 React 的优点。它是全局的,而且效率很高。

这篇笔记是一个例子,它使用 ES5 Javascript 和 webpack 实现了两个小型 Vue 应用程序的版本。因为 VueVue CLI 有清晰的文档,这个例子可能不会为您提供太多价值。它仅仅用于对 Vue 进行小型评估。如果您想快速入门并想获得一个正在运行的示例,可以下载此示例。

Node 应用程序

附带的是一个简单的 Node 应用程序。该应用程序的唯一目的是服务于“vue-example”目录中的静态内容,该目录包含两个 Vue SPA 应用程序。Node 应用程序的入口是“app.js”文件。

var express = require('express'),
    http = require('http'),
    path = require('path');
    
var app = express();
    
app.set('port', 3000);
    
app.use(function (req, res, next) {
    res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
    res.header('Expires', '-1');
    res.header('Pragma', 'no-cache');
    next();
});
    
app.use(express.static(path.join(__dirname, 'vue-example')));
    
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

要运行该应用程序,您需要在应用程序的根目录中发出以下命令来安装“node_modules”。

npm install

然后,您可以通过以下命令启动 Node 应用程序。

node app.js

ES5 Javascript Vue SPA

在“plain-version”目录中,我按照 Vue 文档的说明创建了一个简单的 Vue SPA。它使用 ES5 Javascript 实现,可以直接加载到浏览器中。为了简单起见,所有 CSS、Javascript、HTML 和 Vue 模板都包含在“index.html”文件中。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>vue-example-plain</title>
    
<style type="text/css">
    * {
        box-sizing: border-box;
    }
    
    .container {
        width: 400px;
        height: 400px;
        border: 1px solid red;
        border-radius: 6px;
        display: flex;
          flex-wrap: wrap;
          justify-content: space-evenly;
        align-content: flex-start;
    }
    
    .item {
        width: 80px;
        height: 80px;
        border-radius: 50%;
        background-color: blue;
        margin-top: 12px;
        cursor: pointer;
    }
    
    .item.red {
        background-color: red;
    }
</style>
    
<script src="https://cdn.jsdelivr.net.cn/npm/vue"></script>
    
<script type="text/x-template" id="container-template">
    <div class="container">
        <item v-for="(item, index) in items"
            v-on:itemIsClicked="itemIsClicked"
            v-bind:index="index"
            v-bind:value="items[index]"></item>
        <div style="width: 200px; height: 0px;"></div>
    <div>
</script>
    
<script type="text/x-template" id="item-template">
    <div class="item" v-on:click="itemClicked"
        v-bind:class="{red: value == 1}"></div>
</script>
    
<script type="text/javascript">
    
let item = {
    template: '#item-template',
    props: ['index', 'value'],
    methods: {
        itemClicked: function() {
            this.$emit('itemIsClicked', this.index);
        }
    }
};
    
let container = {
    template: '#container-template',
    components: {
        'item': item
    },
    data: function() {
        return {
            items: new Array(16).fill(0)
        };
    },
    methods: {
        itemIsClicked: function(i) {
            let v = this.items[i];            
            this.$set(this.items, i, (v == 0)? 1:0);
        }
    }
};
    
window.onload = function() {
    let app = new Vue({
        el: '#app',
        components: { 'container': container }
    });
};
    
</script>
    
</head>
<body>
<div id="app"><container></container></div>
</body>
</html>

根据 Vue 文档中的说明,Vue 库是从 CDN “https://cdn.jsdelivr.net.cn/npm/vue” 链接的。这个简单的 Vue SPA 有两个 Vue 组件。

  • “item”组件是一个子组件,用于显示一个圆圈;
  • “container”组件是父组件。它使用 “v-for” 绑定在其内部添加 16 个项目。它还接收来自子组件的事件来切换圆圈的颜色;
  • SPA 在 “window.onload” 事件中被初始化到 “<div id="app">” 中。

这个 SPA 很简单,但它实际上回答了创建 Vue 应用程序时最常被问到的问题,如下所示。

  • 如何初始化 Vue 应用程序;
  • 如何使用子组件;
  • 如何将数据作为 props 传递给子组件;
  • 如何将 CSS 类绑定到组件的数据或 props;
  • 如何挂钩到 Vue 组件的用户事件;
  • 如何从子组件 “$emit” 事件到父组件;
  • 如何更新数据中的某个条目,如果它是 Array 类型,以便 Vue 在条目更改时能够感知到。

当 Node 应用程序运行时,您可以通过 URL - “https://:3000/plain-version/” 访问 Vue SPA。

如果您单击圆圈,您应该会看到颜色按预期切换。

Webpack Vue SPA

在 Vue 文档的帮助下,使用纯 ES5 Javascript 通过 Vue 创建 SPA 是一个简单而愉快的体验。它轻量级且运行速度快。因为 Vue 是轻量级的,所以 Vue 应用程序也很容易维护。但是,如果您是固执己见的人,您可能不喜欢这样一个事实:您可以编写仅在 Web 浏览器中运行而无需转换的 ES5 Javascript 应用程序。您也可能觉得 ES5 Vue 应用程序的模块化程度较低。为了使其模块化,Vue 实际上支持 webpack。

在“wpack-version”目录中,我创建了一个几乎完全相同的 Vue SPA,它使用了 webpack。正如 Vue 文档所建议的,使用 webpack 创建 Vue 应用程序的最佳方法是 Vue CLI。实际上,这个 SPA 是由 Vue CLI 创建的。我只是对其进行了一些小的修改。

NPM 依赖

要将 webpack 用于 Vue 应用程序,我们需要一组 NPM 依赖。现在让我们看看“wpack-version”目录中的“package.json”文件。

{
  "name": "a-vue-example-wpack",
  "description": "a-vue-example-wpack-version",
  "version": "1.0.0",
  "author": "Song Li",
  "license": "MIT",
  "private": true,
  "scripts": {
      "build-dev": "cross-env NODE_ENV=dev webpack --color --watch",
    "build-prod": "cross-env NODE_ENV=production webpack --hide-modules"
  },
  "dependencies": {
    "vue": "^2.5.11"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.0",
    "babel-preset-stage-3": "^6.24.1",
    "cross-env": "^5.0.5",
    "css-loader": "^0.28.7",
    "file-loader": "^1.1.4",
    "vue-loader": "^13.0.5",
    "vue-template-compiler": "^2.4.4",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.1",
    "url-loader": "0.6.2"
  }
}

Vue 组件

当使用 webpack 编写 Vue 应用程序时,我们需要将每个组件创建为一个 “vue” 文件。以下是 “item.vue” 文件。

<template>
    <div class="item" v-on:click="itemClicked"
        v-bind:class="{red: value == 1}"></div>
</template>
    
<script>
export default {
    name: 'item',
    props: ['index', 'value'],
    methods: {
        itemClicked: function() {
            this.$emit('itemIsClicked', this.index);
        }
    }
};
</script>
    
<style scoped>
    .item {
        width: 80px;
        height: 80px;
        border-radius: 50%;
        background-color: blue;
        margin-top: 12px;
        cursor: pointer;
    }
    
    .item.red {
        background-color: red;
    }
</style>

然后,“item”组件被导入并在 “container.vue” 文件中的“container”组件中使用。

<template>
    <div class="container">
        <img src="./assets/logo.png">
        <item v-for="(item, index) in items"
            v-on:itemIsClicked="itemIsClicked"
            v-bind:key="index"
            v-bind:index="index"
            v-bind:value="items[index]"></item>
        <div style="width: 200px; height: 0px;"></div>
    </div>
</template>
    
<script>
import item from './item.vue';

export default {
    name: 'container',
    components: {
        'item': item
    },
    data: function() {
        return {
            items: new Array(16).fill(0)
        };
    },
    methods: {
        itemIsClicked: function(i) {
            let v = this.items[i];            
            this.$set(this.items, i, (v == 0)? 1:0);
        }
    }
};
</script>
    
<style scoped>
    .container {
        width: 400px;
        height: 600px;
        border: 1px solid red;
        border-radius: 6px;
        display: flex;
          flex-wrap: wrap;
          justify-content: space-evenly;
        align-content: flex-start;
    }
    
    .container>img {
        width: 350px;
    }
</style>

“container”组件被导入并在 “main.js” 文件中启动。

import Vue from 'vue';
import container from './container.vue';
    
new Vue({ el: '#app', render: h => h(container) });

“webpack.config.js”

“webpack.config.js” 文件配置 webpack 如何打包应用程序。

var path = require('path')
var webpack = require('webpack')
    
module.exports = {
  entry: './app/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/wpack-version/dist/', filename: 'build.js'
  },
  module: {
    rules: [
      { test: /\.css$/, use: [ 'vue-style-loader', 'css-loader' ] },
      { test: /\.vue$/, loader: 'vue-loader', options: { loaders: {} } },
      { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } },
      {
          test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
          use: [{ loader: 'url-loader', options: { limit: 10240 } }]
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }, extensions: ['*', '.js', '.vue', '.json']
  }
};
    
if (process.env.NODE_ENV === 'production') {
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.optimize.UglifyJsPlugin({})])}

“.babelrc” 文件

这个小的 “.babelrc” 文件是一个隐藏文件。但它需要位于“wpack-version”目录中。没有它,您将无法成功进行生产 webpack 构建。

{
  "presets": [
    ["env", { "modules": false }],
    "stage-3"
  ]
}

“index.html”

由 webpack 在 “/wpack-version/dist/” 目录中构建的 “build.js” 文件将被加载到 “index.html” 中供浏览器渲染应用程序。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>vue-example-wpack</title>

<style type="text/css">
* {
    box-sizing: border-box;
}
</style>
</head>
<body>
<div id="app"></div>
    
<script src="/wpack-version/dist/build.js"></script>
</body>
</html

构建并运行

为了构建“wpack-version”目录中的 SPA,您需要从 Node 应用程序的根目录发出以下命令来安装“wpack-version”目录中的 “node_modules”。

npm -prefix vue-example/wpack-version install

要进行开发 webpack 构建,您可以发出以下命令。它将构建 SPA 并设置一个监视器。每当您对相关文件进行任何更改时,监视器将触发另一个构建,以节省您手动触发新构建以反映所做更改的精力。

npm -prefix vue-example/wpack-version run build-dev

如果您想最小化您的 webpack 包,可以使用以下命令触发生产构建。

npm -prefix vue-example/wpack-version run build-prod

生产构建仅应用于生产发布。最小化过程是一个缓慢的过程。在开发过程中,当您需要不断地进行更改和重新构建 SPA 时,所需的时间似乎太长了。

当 Node 应用程序运行时,您可以通过 URL - “https://:3000/wpack-version/” 访问 SPA 的 webpack 版本。如果您在浏览器中检查 Vue 徽标,您会发现 webpack 不仅打包了所有 Vue 组件,还实际上将一个图像文件转换为 Base64 格式并将其添加到包中。

关注点

  • 这是一篇关于 Vue & Webpack 的笔记;
  • 希望您喜欢我的博文,并希望这篇笔记能以某种方式帮助您。

历史

首次修订 - 2017 年 12 月 26 日

© . All rights reserved.