前言

研究了下别人的 vue 多页面框架, 都是直接复制 package.json 文件,然后在本地 npm install 一下即可, 或者使用官网 vue-cli 工具生成一个项目, 觉得这样虽然看的懂, 但是记不住, 因此有必要从零开始搭建一个使用 .vue 作为组件的项目练习一下, 因此有了这个项目.

既然使用了 .vue 组件, 就不能像之前使用 jQuery 一样把 vue.js 引入页面中, 而是使用配套的 webpack + babel + 各种 loader 工具编译 .vue 文件, 再打包生成 html.

FBI warning

切记 : 因为是最基本的初体验, 所以一些正式开发中必装的 loader 和 plugin 就没有装, 因为只是想按照官方教程手动敲出来加深印象, 特别是进阶教程中比较麻烦的父子组件传参, 作用域插槽, 递归组件以及 slot等. 因此这个配置不可能作为正式开发的参照配置, 只可作为了解 vue 组件工作原理的练手项目.

配置说明

以下配置的详细说明在后面可以找到, 不想看的话直接复制下面的 package.json 即可, 但是为了加深印象还是建议手动敲一遍.

废话不多说, 开始.

首先, 既然是 webpack+vue, 那相应的安装包少不了, 这里我们使用 vue@2.2.4 和 webpack@1.12.2:

npm install webpack@1.12.2 vue@2.2.4 --save-dev

然后是 babel 和相应的 loader, 这里我们使用 es2015 这个配置, 用最新的就好:

npm install babel bebel-core babel-loader babel-preset-es2015 --save-dev

然后是 webpack 的必装 loadercss-loader 用来处理 css 中 url() 的资源, style-loader 用来将 require 的 css 抽出放到 style 标签中, 然后加到页面 head 部分. html-webpack-plugin 用来将入口文件 js变成 html, 入口文件中的各种资源由各种 loader 处理后插入到它生成的 html 中, extract-text-webpack-plugin 用来将被 js 通过 style 标签 append 到 head 中的样式抽出到单独的 .css 文件中:

npm install css-loader style-loader html-webpack-plugin extract-text-webpack-plugin@1.0.1 --save-dev

然后是 vue 相关的东西, 因为一个 .vue 里面有至少有三个标签 template/style/script, 因此需要三个 loader 来处理, 再加上一个总的 vue-loader, 就是四个 loader ,这里:

vue-html-loader 是 webpack 的官方 html-loader 的 fork, 作者放到这里只是为了能在 webpack.config.js 中的 module.export.vue 对象上使用 html 选项来单独配置 vue 的 相关 html(本项目安装 vue-loader 即可, 这里只是顺带安装说明一下);

vue-style-loader 用来处理 .vue 文件中 style 中的内容, 是 webpack 的官方 style-loader 的 fork(本项目安装 vue-loader 即可, 这里只是顺带安装说明一下);

vue-template-compiler 用来处理 .vue 文件中 template 中的内容, 除非是用它编译后的文件做其他事情才需要单独配置(即写 build tools, 否则这个不是必须的, 因为 vue-loader 已经默认使用它了)(本项目安装 vue-loader 即可, 这里只是顺带安装说明一下);

vue-loader 用来处理 .vue 后缀的内容, 在遇到相关的内容时, 会调用上述三个相关的 loader 来处理.

npm install vue-html-loader vue-loader vue-style-loader vue-template-compiler --save-dev

最后就是开发用的 webpack-dev-server, 这里我们安装 1.12.1 版本:

npm install webpack-dev-server --save-dev

下面是总的 package.json 配置文件, 而具体的每个 package.json 字段的含义, 可以查看这个网站,

{ "name": "vue-components", "version": "0.0.1", "description": "vue components test", "main": "app/app.js","scripts": { "dev": "webpack-dev-server --hot", "build": "webpack" }, "keywords": [ "vue", "components" ],"author": "xheldon", "license": "MIT", "dependencies": { "vue": "^2.2.4" }, "devDependencies": { "babel":"^6.23.0", "babel-core": "^6.24.0", "babel-loader": "^6.4.1", "babel-preset-es2015": "^6.24.0", "css-loader":"^0.27.3", "extract-text-webpack-plugin": "^1.0.1", "html-webpack-plugin": "^2.28.0", "style-loader":"^0.16.0", "vue-html-loader": "^1.2.4", "vue-loader": "^11.3.1", "vue-style-loader": "^2.0.4", "vue-template-compiler": "^2.2.4", "webpack": "^1.12.2", "webpack-dev-server": "^1.12.1" } }

项目说明

Ok, 依赖安装完了, 接下来看下 webpack 配置, 因为是想尽快测试 vue 官方文档的组件部分, 所以一切从简了:

var path = require('path'); var webpack = require('webpack'); var HtmlwebpackPlugin = require('html-webpack-plugin'); // 常用配置,项目较小不抽出了 var ROOT_PATH = path.resolve(__dirname);//根路径 var APP_PATH =path.resolve(ROOT_PATH, 'app');//开发路径 var BUILD_PATH = path.resolve(ROOT_PATH, 'build');//输出路径 varExtractTextPlugin= require('extract-text-webpack-plugin'); module.exports = { entry: { app:path.resolve(APP_PATH, 'app.js') }, output: { path: BUILD_PATH, filename: 'bundle.js'//因为只有一个入口文件, 因此直接写死了 }, resolve: { alias: { //注意, 这里导入的是/node_module/vue/dist/vue.js, 跟 vue-router.js 的不同vue: 'vue/dist/vue.js' } }, //开启 dev source map devtool: 'eval-source-map', //开启 dev server devServer: {historyApiFallback: true, hot: true,//HMR inline: true, progress: true }, module: { loaders:[ { test:/\.vue$/, loader: 'vue' }, { test: /\.css$/, loader: ExtractTextPlugin.extract('css-loader') }, { test:/\.js$/, loader: 'babel', include: ROOT_PATH, exclude: /node_modules/ }, { test: /\.html$/, loader: 'vue-html'} ] }, vue:{ loaders: { css: ExtractTextPlugin.extract('css-loader') } }, plugins:[ new HtmlwebpackPlugin({title: 'Vue component test', filename: 'this_is_final_filename_address_you_visit_in_browser.html',//生成的最终文件名 template: 'app/this_is_main_page_which_you_put_components_into.html',//放置组件的地方, 一般是一个 body 下一个孤零零的 app 标签即可. inject: true }) ] };

关于这个配置, 有点东西需要说一下.

从上往下, 首先是 alias , 使用过 vue-router的人可能不需要这个配置, 但是使用 .vue 组件的项目必须这个配置,因为需要指定使用的 vue 的 js 类型, 看下本项目下 node_module/vue/dist/ 文件夹下的文件, 有 vue.js 和 vue.common.js 两种, 其中 vue 编译 template 组件的时候是需要一个 compiler.js 的, 目的是把 template 中的 html 内容编译成 render 函数:

编译前:

<div id="app"> Hello </div> <script> new Vue({ el: '#app', data: {who: 'Vue'} }) </script>

编译后:

<div id="app"></div> <script> new Vue({ el: '#app', render: function () { with (this) { __h__('div',{staticAttrs:{"id":"app"}}, [("\n Hello "+__toString__(who)+"\n")], '' ) } }, data: {who: 'Vue'} }) </script>

而 vue 使用 compiler 编译 template 后的 js 在运行的时候发现有 render 函数的话就直接执行 rendertemplate 字段下的内容会被忽略. 而执行编译后的 render 的任务,是由 vue.common.js 完成的.因此:

vue.js = vue.common.js + compiler.js

所以, 如果你使用的是 vue-router, 它的 package.json 中的 main 字段是指向 node_module/dist/vue.common.js 的, 如果你直接复制这个到你的项目下, 运行的时候会提示你类似于 vue.common.js 的 runtime错误之类的信息.

其次需要说的是这个 css-loader 和 style-loader 以及 vue-style-loader, 有了 style-loader 为何还要个 vue-style-loader 呢? 看了下 vue-style-loader 的说明, 明白了其仅仅是一个 style-loader 的 fork, 但是为了单独处理 .vue 文件, 同时为了让用户配置 vue 更清晰, 将其加到了 webpack@1.x 的配置文件中的 vue 字段中. 可以通过将 extract-text-webpack-plugin 插件的配置从:

vue:{ loaders: { css: ExtractTextPlugin.extract('vue-style-loader','css-loader') } }

改成:

vue:{ loaders: { css: ExtractTextPlugin.extract('style-loader','css-loader') } }

发现编译后的结果一样证实.

而默认情况下, vue-loader 是自动使用 vue-style-loader 的, 所以如果你不在 .vue 文件中 @import 任何 css , 那么你不需要手动把 vue-loader-style 放到 vue.loaders 字段中. vue-loader 会自动处理 .vue 文件中的 style 标签中的内容, 并将其放到 style 标签中插入页面. 而如果你需要在 .vue 文件中的 style 标签内 @import css 文件, 那么你就需要在 module.exports.vue 单独配置.可以通过把 vue 字段的 vue-style-loader 去掉来测试:

module: { loaders:[ { test: /\.vue$/, loader: 'vue' } ] }, vue:{ // loaders: { // css: 'vue-style-loader!css-loader' // } }

此外, 入口文件 js 中的 require('xxx.css') 是默认的 module.exports.module.loader 处理的, 这点可以通过在默认 loader 中使用 extract 插件, 而在 module.exports.vue 中不使用 extract 插件证实, 因为从结果可以发现, 入口文件中的 css 被提取了, 但是 .vue 中的 @import 来的 css 没有被提取.

而如果你需要 将入口文件中 require 来的 css 文件单独提取出来, 那么你就需要在 module.exports.module.loader 设置 extract-text-webpack-plugin 了.

注意: vue-style-loader 放在 module.exports.vue.loaders 字段中是为了能提取出 .vue 文件中的 style 标签内容到一个单独的 .css 文件 link 在页面中, 把 style-loader 和 css-loader 放在默认的 module.exports.module.loaders 中, 对处理 vue 中的 style 标签内容无效————起码在 Vue 1.x 版本和 webpack 1.x 版本无效, webpack 2.x 版本移除了第三方的字段, 限制在 module.export 中随意添加字段.

最后, css-loader 和 style-loader 总是写在一起的, 因为 css-loader 的作用是 resolve css 文件中的 @import 和 属性值 url() 中的依赖关系, 单独写其实是没什么用的. style-loader 才是处理 css, 并将其打包到 js 中, 最后以 <style> 标签的形式插入到 head (插入位置可配置)中的 loader.

最后讲讲 extract-text-webpack-plugin, 其接受三个参数:

第一个参数是可选参数, 传入一个 loader, 当 css 样式没有被抽取的时候可以使用该 loader. 第二个参数则是用于编译解析的 css 文件 loader, 很明显这个是必须传入的, 就像上述例子的 css-loader. 第三个参数是一些额外的备选项, 貌似目前只有传入 publicPath, 用于当前 loader 的路径.

那什么时候需要传入第一个参数呢,那就得明白什么时候样式不会被抽取出来. 了解过 code splitting 的同学便会知道,我们有些代码在加载页面的时候不会被使用时, 使用 code splitting, 可以实现将这部分不会使用的代码分离出去, 独立成一个单独的文件,实现按需加载.

那么如果在这些分离出去的代码中如果有使用 require 引入样式文件, 那么使用 ExtractTextPlugin 这部分样式代码是不会被抽取出来的. 这部分不会抽取出来的代码, 可以使用 loader 做一些处理, 这就是 ExtractTextPlugin.extract 第一个参数的作用.

OK, 聊完了配置文件, 再说说这个项目是怎么工作的.

一图胜千言, 上图, 首先是代码界面:

(若图片显示较小请在右键在新标签页单独查看)

看箭头所示就明白啦, 首先一个页面是至少有一个组件的, 这个我直接就一个页面一个组件来写了, 没有 import 其他的组件.

因此, 一个页面下是至少三个文件的, .vue 文件, .js 入口文件, 和 .html, 组件插入的文件.

html 中写一个组件 app 的名字, 入口文件实例化一个 vue, 然后使用 app 这个组件, 同时这个叫做 app 组件的模板来自 index.vue, 组件对应的 css 和 js 以及 mvvm 的特色:数据绑定也写在 index.vue 里面.

有同学会疑惑, 入口文件 js 是怎么找到同目录的 html 文件的呢? 其实这个在 webpack.config.js 配置文件就已经写好了:

plugins:[ new HtmlwebpackPlugin({ title: 'Vue component test', filename:'this_is_final_filename_address_you_visit_in_browser.html',//生成的最终文件名 template:'app/this_is_main_page_which_you_put_components_into.html',//放置组件的地方, 一般是一个 body 下一个孤零零的 app 标签即可. inject: true }) ]

这个 html 生成的插件告诉 js 入口文件, 所需要的模板来自 app 下的 xxx.html, 而最后打包的 bundle.js也是 inject 这个里面, 再生成最终的页面.

还有同学会问, 在入口 js 文件中, vue 实例化的时候用到的 components.App 到底是在编译过程就找到了 this_is_main_page_which_you_put_components_into.html 文件中的 <app> 组件引用, 还是在 runtime的时候, 从最终打包的 bundle.js 中运行, 然后寻找 this_is_final_filename_address_you_visit_in_browser.html 页面中的 <app> 标签呢? 答案是后者, 因为刚才说的 HtmlwebpackPlugin 插件只负责生成 html 和注入打包后的 bundle.js, 而 vue 被打包进了 bundle.js 之后实例化 vue 时候才会寻找 <app> 标签.

这么一看, 和直接在 script 标签中引用 vue.js 文件再渲染的效果是一样的, 只是 webpack 这种开发方式帮我们分离了组件, 使开发过程的代码/组件结构更清晰, 而且直接引用 vue.js 是前端 runtime render, 一个是 compiler render 之后直接执行, 后者效率更高.

之后是效果页面:

 (若图片显示较小请在右键在新标签页单独查看)

看图就明白什么意思啦.

还有不明白的请看文档.

[转] Vue + Webpack 组件式开发(练习环境)的更多相关文章

  1. 组件式开发(Vue)

    什么是组件式开发: 组件式开发就是将单个组件组合起来,形成一个大的组件进行页面的开发完成 什么是复合型组件: 复合型组件就是将相同的功能写成一个公用的组件(单元组件),供其他组件使用,就类似于后台开发 ...

  2. Agile.Net 组件式开发平台 - 组件开发示例

    所谓组件式开发平台,它所有的功能模块都是以组件的形式扩展的,下面我来演示一个简单的组件开发例程. Agile.Net开发管理平台项目,已经托管在开源中国码云平台(http://git.oschina. ...

  3. Agile.Net 组件式开发平台 - 平台系统介绍

    平台介绍 Agile.Net 组件式开发平台是一款针对企业级产品的开发框架,平台架构基于SOA服务体系,多层组件式架构打造.平台提供企业应用开发所需的诸如ORM.IOC.WCF.EBS.SOA等分布式 ...

  4. PIE SDK组件式开发综合运用示例

    1. 功能概述 关于PIE SDK的功能开发,在我们的博客上已经分门别类的进行了展示,点击PIESat博客就可以访问,为了初学者入门,本章节将对从PIE SDK组件式二次开发如何搭建界面.如何综合开发 ...

  5. Agile.Net 组件式开发平台 - 驱动开发示例

    首先讲一下概念,此驱动非彼驱动.在Agle.Net中我们将组件规划成两种类型,一种是基于业务的窗体组件,一种是提供扩展功能的驱动组件. 打个比方例如一般系统中需要提供身份证读卡功能,然而市面上有很多种 ...

  6. Agile.Net 组件式开发平台 - 服务开发示例

    在上一篇文章中已经讲解了组件的开发,这篇文章讲解平台服务开发. Agile.Net开发管理平台项目,已经托管在开源中国码云平台(http://git.oschina.net) 登陆码云平台进入项目主页 ...

  7. 基于TypeScript的FineUIMvc组件式开发(开头篇)

    了解FineUIMvc的都知道,FineUIMvc中采用了大量的IFrame框架,对于IFrame的优缺点网上也有很多的讨论,这里我要说它的一个优点“有助于隔离代码逻辑”,这也是FineUIMvc官网 ...

  8. vue.js组件化开发实践

    前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然 ...

  9. 【06】Vue 之 组件化开发

    组件其实就是一个拥有样式.动画.js逻辑.HTML结构的综合块.前端组件化确实让大的前端团队更高效的开发前端项目.而作为前端比较流行的框架之一,Vue的组件和也做的非常彻底,而且有自己的特色.尤其是她 ...

随机推荐

  1. DP擎天

    DP! 黄题: 洛谷P2101 命运石之门的选择 假装是DP(分治 + ST表) CF 982C Cut 'em all! 树形贪心 洛谷P1020 导弹拦截 单调队列水题 绿题: 洛谷P1594 护 ...

  2. rsync实时同步服务部署

    部署rsync服务 一.需求:把客户端文件同步到服务端指定位置服务端:备份服务器为 172.16.3.164客户端:推送服务器为 172.16.3.94 二.基础知识: rsync 分为服务器端.客户 ...

  3. JS兼容问题

    //1.滚动条到顶端的距离 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //2.滚动条 ...

  4. Qt: 非阻塞时间延迟;

    1.使用时间耗损循环: #include <QTime> ... QTime delayTime = QTime::currentTime().addMSecs(1000); while( ...

  5. JavaScript IIEF 模仿块级作用域

    前言 JavaScript没有块级作用域的概念.但是通过IIEF 立即执行函数我们可以实现块级作用域. function outputNumbers(count){ for (var i=0; i & ...

  6. SpringBoot系列: SpringBoot Web项目中使用Shiro 之二

    ==================================Shiro 的加深理解:==================================1. Shiro 和 Spring 系组 ...

  7. [物理学与PDEs]第5章习题3 第二 Piola 应力张量的对称性

    试证明: 在物质描述下, 动量矩守恒定律等价于第二 Piola 应力张量的对称性. 证明: 由 $$\beex \bea \int_{G_t}\rho\sex{{\bf y}\times\cfrac{ ...

  8. [物理学与PDEs]第1章第7节 媒质中的 Maxwell 方程组 7.2 媒质交界面上的条件

    通过 Maxwell 方程组的积分形式易在交界面上各量应满足交界面条件: $$\beex \bea \sez{{\bf D}}\cdot{\bf n}=\omega_f,&\sex{\omeg ...

  9. pc端,移动端css重置样式

    pc: html{font-family:"Microsoft YaHei UI","Microsoft YaHei",sans-serif;-ms-text- ...

  10. monkeyrunner环境配置

    1.安装JDK.android  SDK.Python (安装完成后,配置环境变量:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%ANDROID_HOME%\platfor ...