本节目录
之前我们获取dom是通过原生js或者jQuery的选择器来获取的,那么vue也给我们提供了一些获取dom的方法。
方式:给标签或者组件添加ref属性,将来我们通过this.$refs属性可以获取到这个标签或者组件。
<div ref="chao"></div> <p ref="a"></p> <Home ref="b"></Home> 子组件 <script> this.$refs.chao 获取原始的DOM对象 this.$refs.b 父组件中获取的是子组件实例化对象 </script>
简单使用一下:让某个input标签在浏览器加载完之后,自动获取到光标(焦点)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> </div> </body> <script src="vue.js"></script> <script src="vue-router.js"></script> <script> Vue.component('Test',{ data(){ return{ } }, //如果这个全局组件中没有template或者render方法,会报个错误给你,这个补充和咱们今天的内容没有关系昂 template: ` <div>这是全局Test组件</div> `, }) ; let App = { data(){ return{ } }, template:` <div> <input ref="username" type="text"> <Test ref="abc"></Test> </div> `, //让上面的input默认是获取焦点的状态,刚开始打开页面的时候,input是没有获取到焦点(光标)的状态 mounted(){ //操作dom的方式 console.log(this.$refs.username); this.$refs.username.focus();//focus()方法是原生js方法,让前面这个input标签自动获取焦点用的。 //操作组件的方式 console.log(this.$refs);//所有含有ref属性的标签或者组件:{username: input, abc: VueComponent},发现就是个自定义属性(字典) console.log(this.$refs.abc);//VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} console.log(this.$refs['abc']); //VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …} console.log(this.$refs.abc.$parent);//就是this,abc的父级组件App组件 console.log(this.$children);//就是this当前的组件的所有子组件,顺序是按照template中使用组件的时候的顺序,还有好多其他的方法等等,以后再说 //循环所有含有ref属性的标签或者组件 for (let key in this.$refs){ console.log(this.$refs[key]); } }, }; let vm = new Vue({ el:'#app', data(){ return{ } }, template:` <div> <App></App> </div> `, components:{ App, } }) </script> </html>
首先我们先到YEOMAN中看一个图,这个YEOMAN是集合了前端所有的脚手架,包括服务器的、js的、jQuery的、react、angular、vue等等的脚手架都在这里面有,网址:https://yeoman.io/,我们不用去研究里面的内容,我只想让大家看一个图:
这个图的意思就是每个工程师都拿着自己现成的工具来做火箭这个项目,将来我们要通过vue-cli来做组件化开发,在组件化开发中就使用到了我们的webpack工具(这是前端的工具),前端最开始用的grunt后来是gulp,再后来就是我们现在主流的webpack工具了,那到底这个工具做了什么,我们往后看。
webpack介绍
webpack是一个现代JavaScript应用程序的静态模块打包器(比如python中一个.py文件就是一个模块,那么前端中一个js、html、css等文件就可以称为一个模块)。当 webpack 处理应用程序时,它会递归地构建一个*依赖关系图(dependency graph)*,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 *bundle*。这个webpack已经内置在了vue-cli中,所以我们下载安装了vue-cli之后就能直接使用webpack工具了。接下来我们看一下下面这张图,图片来自于webpack官网(中文文档)https://www.webpackjs.com/
历史介绍(了解)
- 2009年初,commonjs异步模块规范还未出来,此时前端开发人员编写的代码都是非模块化的,使用闭包来完成类似模块化的开发。commonjs是在nodejs中产生的,是服务器语言,也就是有他之后,前端语言就可以做服务端的开发了。 - 那个时候开发人员经常需要十分留意文件加载顺序所带来的依赖问题,比如你使用很多的script标签,然后每个标签的src属性指向一个js文件,那么要注意这些标签的引入顺序,并且有一个js文件出问题了,后面的js文件都加载不上,这就是一个同步加载的问题。 - 与此同时 nodejs开启了js全栈大门,而requirejs在国外也带动着前端逐步实现模块化 - 同时国内seajs也进行了大力推广,中国人淘宝玉伯写的,他的规范叫做AMD - AMD 规范 ,具体实现是requirejs define('模块id',[模块依赖1,模块依赖2],function(){ return ;}) , ajax请求文件并加载,例如:你有三个js文件,a.js、b.js、c.js,使用c.js的时候需要依赖a和b,那么写法 define('c.js',(a.js,b.js),function(){}) - Commonjs || CMD规范 - commonjs和cmd非常相似的 - cmd require(类似于import,引入的意思)/module.exports(输出的意思)
- commonjs是js在后端语言的规范: 模块、文件操作、操作系统底层 - CMD 仅仅是模块定义 - UMD 通用模块定义,一种既能兼容amd也能兼容commonjs 也能兼容浏览器环境运行的万能代码 - npm/bower集中包管理的方式备受青睐,12年browserify/webpack诞生 - npm 是可以下载前后端的js代码475000个包 - bower 只能下载前端的js代码,bower 在下载bootstrap的时候会自动的下载jquery - browserify 解决让require可以运行在浏览器,分析require的关系,组装代码 - webpack 打包工具,占市场主流
后面的内容需要我们学习~~~
1. require和module.exports
require和module.exports的使用举例,注意这两个方法必须在nodejs的环境下才能使用,不然浏览器是识别不了这两个方法,就会报错,并且我们平时创建的.js文件在nodejs环境中会识别成nodejs类型的文件,不过没关系,基本都是支持的,创建文件的时候还是xx.js文件。
假如我们现在有三个文件,index.html和time.js和index.js文件
index.html最开始引入js文件的方法是这样的:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 同步引入的方式 --> <script src="index.js"></script> <script src="time.js"></script> </body> </html>
然后我们通过CMD规范来写一下通过require和module.exports两个方法实现模块化开发
index.js内容如下:
// var person1 = { // name:'张三', // // }; // //可以抛出,自定义对象,变量,函数等等内容,但是这些js文件的这样的用法必须在nodejs环境下执行,而nodejs是从原生js加工出来的,支持我们写js语言,但是有些功能不支持,比如说alert,window弹框等操作 // var person2 = { // name:'李四', // }; //将我们在index.js中的person抛出,然后其他js文件就可以通过require方法来引用了 // module.exports = person1; function add(){ console.log('add函数'); } module.exports = add; //将add函数抛出,其他js文件才能通过require引入使用,将来我们就将我们的组件写到一个js文件中,通过这个抛出组件,然后其他文件中require来使用这个组件
time.js内容如下,time.js文件的运行需要index.js中的内容
var p = require('./index.js'); // console.log(p.name); p();//执行一下index.js文件中引入的add函数
说了,我们需要在nodejs的环境下使用这两个方法,那么我们在pycharm这个IDE的终端来调用一下node来执行一下time.js文件。
先看我的文件目录结构:
然后终端切换到这个nodejs模块的文件夹下,执行一下下面的指令:就得到了index.js函数中的执行结果。
上面就是CMD的规范,其实vue更加希望我们使用es6的module(模块)规范,我们来看看module规范的语法。
一个export抛出,一个import引入,阮一峰的博客上介绍的比较详细,就在他es6的那个博客里面有个module语法的那个章节里面有。
好,那么我们先来学一下export和import的应用:
2.export和import
简单铺垫:历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require
、Python 的import
,甚至就连 CSS 都有@import
,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块 let { stat, exists, readFile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
上面代码的实质是整体加载fs
模块(即加载fs
的所有方法),生成一个对象(_fs
),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。
ES6 模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令输入。
// ES6模块 import { stat, exists, readFile } from 'fs';
上面代码的实质是从fs
模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
export命令
模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。下面是一个 JS 文件,里面使用export
命令输出变量。
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'; export var year = 1958;
上面代码是profile.js
文件,保存了用户信息。ES6 将其视为一个模块,里面用export
命令对外部输出了三个变量。
export
的写法,除了像上面这样,还有另外一种。
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
上面代码在export
命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var
语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
export
命令除了输出变量,还可以输出函数或类(class)。
export function multiply(x, y) { return x * y; };
上面代码对外输出一个函数multiply
。
通常情况下,export
输出的变量就是本来的名字,但是可以使用as
关键字重命名。
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };
上面代码使用as
关键字,重命名了函数v1
和v2
的对外接口。重命名后,v2
可以用不同的名字输出两次。
需要特别注意的是,export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错 export 1; // 报错 var m = 1; export m;
上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量m
,还是直接输出 1。1
只是一个值,不是接口。正确的写法是下面这样。
// 写法一 export var m = 1; // 写法二 var m = 1; export {m}; // 写法三 var n = 1; export {n as m};
上面三种写法都是正确的,规定了对外的接口m
。其他脚本可以通过这个接口,取到值1
。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
同样的,function
和class
的输出,也必须遵守这样的写法。
// 报错 function f() {} export f; // 正确 export function f() {}; // 正确 function f() {} export {f};
另外,export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
上面代码输出变量foo
,值为bar
,500 毫秒之后变成baz
。
这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存,不存在动态更新,详见下文《Module 的加载实现》一节。
最后,export
命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的import
命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。
function foo() { export default 'bar' // SyntaxError } foo()
上面代码中,export
语句放在函数之中,结果报错。
import命令
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块。
// main.js import {firstName, lastName, year} from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
上面代码的import
命令,用于加载profile.js
文件,并从中输入变量。import
命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js
)对外接口的名称相同。
如果想为输入的变量重新取一个名字,import
命令要使用as
关键字,将输入的变量重命名。
import { lastName as surname } from './profile.js';
import
命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import {a} from './xxx.js' a = {}; // Syntax Error : 'a' is read-only;
上面代码中,脚本加载了变量a
,对其重新赋值就会报错,因为a
是一个只读的接口。但是,如果a
是一个对象,改写a
的属性是允许的。
import {a} from './xxx.js' a.foo = 'hello'; // 合法操作
上面代码中,a
的属性可以成功改写,并且其他模块也可以读到改写后的值。不过,这种写法很难查错,建议凡是输入的变量,都当作完全只读,轻易不要改变它的属性。
import
后面的from
指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js
后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
import {myMethod} from 'util';
上面代码中,util
是模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块。
注意,import
命令具有提升效果,会提升到整个模块的头部,首先执行。
foo(); import { foo } from 'my_module';
上面的代码不会报错,因为import
的执行早于foo
的调用。这种行为的本质是,import
命令是编译阶段执行的,在代码运行之前。
由于import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错 import { 'f' + 'oo' } from 'my_module'; // 报错 let module = 'my_module'; import { foo } from module; // 报错 if (x === 1) { import { foo } from 'module1'; } else { import { foo } from 'module2'; }
上面三种写法都会报错,因为它们用到了表达式、变量和if
结构。在静态分析阶段,这些语法都是没法得到值的。
最后,import
语句会执行所加载的模块,因此可以有下面的写法。
import 'lodash';
上面代码仅仅执行lodash
模块,但是不输入任何值。
如果多次重复执行同一句import
语句,那么只会执行一次,而不会执行多次。
import 'lodash'; import 'lodash';
上面代码加载了两次lodash
,但是只会执行一次。
import { foo } from 'my_module'; import { bar } from 'my_module'; // 等同于 import { foo, bar } from 'my_module';
上面代码中,虽然foo
和bar
在两个语句中加载,但是它们对应的是同一个my_module
实例。也就是说,import
语句是 Singleton 模式。
目前阶段,通过 Babel 转码,CommonJS 模块的require
命令和 ES6 模块的import
命令,可以写在同一个模块里面,但是最好不要这样做。因为import
在静态解析阶段执行,所以它是一个模块之中最早执行的。下面的代码可能不会得到预期结果。
require('core-js/modules/es6.symbol'); require('core-js/modules/es6.promise'); import React from 'React';
模块整体加载
除了指定加载某个输出值,还可以使用整体加载,即用星号(*
)指定一个对象,所有输出值都加载在这个对象上面。
下面是一个circle.js
文件,它输出两个方法area
和circumference
。
// circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; }
现在,加载这个模块。
// main.js import { area, circumference } from './circle'; console.log('圆面积:' + area(4)); console.log('圆周长:' + circumference(14));
上面写法是逐一指定要加载的方法,整体加载的写法如下。
import * as circle from './circle'; console.log('圆面积:' + circle.area(4)); console.log('圆周长:' + circle.circumference(14));
注意,模块整体加载所在的那个对象(上例是circle
),应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的。
import * as circle from './circle'; // 下面两行都是不允许的 circle.foo = 'hello'; circle.area = function () {};
export default命令
从前面的例子可以看出,使用import
命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default
命令,为模块指定默认输出。
// export-default.js export default function () { console.log('foo'); }
上面代码是一个模块文件export-default.js
,它的默认输出是一个函数。
其他模块加载该模块时,import
命令可以为该匿名函数指定任意名字。
// import-default.js import customName from './export-default'; customName(); // 'foo'
上面代码的import
命令,可以用任意名称指向export-default.js
输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import
命令后面,不使用大括号。
export default
命令用在非匿名函数前,也是可以的。
// export-default.js export default function foo() { console.log('foo'); } // 或者写成 function foo() { console.log('foo'); } export default foo;
上面代码中,foo
函数的函数名foo
,在模块外部是无效的。加载的时候,视同匿名函数加载。
下面比较一下默认输出和正常输出。
// 第一组 export default function crc32() { // 输出 // ... } import crc32 from 'crc32'; // 输入 // 第二组 export function crc32() { // 输出 // ... }; import {crc32} from 'crc32'; // 输入
上面代码的两组写法,第一组是使用export default
时,对应的import
语句不需要使用大括号;第二组是不使用export default
时,对应的import
语句需要使用大括号。
export default
命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default
命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default
命令。
本质上,export default
就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。
// modules.js function add(x, y) { return x * y; } export {add as default}; // 等同于 // export default add; // app.js import { default as foo } from 'modules'; // 等同于 // import foo from 'modules';
正是因为export default
命令其实只是输出一个叫做default
的变量,所以它后面不能跟变量声明语句。
// 正确 export var a = 1; // 正确 var a = 1; export default a; // 错误 export default var a = 1;
上面代码中,export default a
的含义是将变量a
的值赋给变量default
。所以,最后一种写法会报错。
同样地,因为export default
命令的本质是将后面的值,赋给default
变量,所以可以直接将一个值写在export default
之后。
// 正确 export default 42; // 报错 export 42;
上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定对外接口为default
。
有了export default
命令,输入模块时就非常直观了,以输入 lodash 模块为例。
import _ from 'lodash';
如果想在一条import
语句中,同时输入默认方法和其他接口,可以写成下面这样。
import _, { each, forEach } from 'lodash';
对应上面代码的export
语句如下。
export default function (obj) { // ··· } export function each(obj, iterator, context) { // ··· } export { each as forEach };
上面代码的最后一行的意思是,暴露出forEach
接口,默认指向each
接口,即forEach
和each
指向同一个方法。
export default
也可以用来输出类。
// MyClass.js export default class { ... } // main.js import MyClass from 'MyClass'; let o = new MyClass();
export 和 import的复合用法
如果在一个模块之中,先输入后输出同一个模块,import
语句可以与export
语句写在一起。
export { foo, bar } from 'my_module'; // 可以简单理解为 import { foo, bar } from 'my_module'; export { foo, bar };
上面代码中,export
和import
语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,foo
和bar
实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo
和bar
。
模块的接口改名和整体输出,也可以采用这种写法。
// 接口改名 export { foo as myFoo } from 'my_module'; // 整体输出 export * from 'my_module';
默认接口的写法如下。
export { default } from 'foo';
具名接口改为默认接口的写法如下。
export { es6 as default } from './someModule'; // 等同于 import { es6 } from './someModule'; export default es6;
同样地,默认接口也可以改名为具名接口。
export { default as es6 } from './someModule';
下面三种import
语句,没有对应的复合写法。
import * as someIdentifier from "someModule"; import someIdentifier from "someModule"; import someIdentifier, { namedIdentifier } from "someModule";
为了做到形式的对称,现在有提案,提出补上这三种复合写法。
export * as someIdentifier from "someModule"; export someIdentifier from "someModule"; export someIdentifier, { namedIdentifier } from "someModule";
好,总结一下,export和export default都是抛出接口的,而import是获取接口的。
下面我们就通过export default先来个例子玩玩:
我们先创建两个文件,main.js文件和index.html文件和module.js文件
index.html内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 只引入main.js,因为main.js用来作为所有js文件的入口文件 --> <script src="main.js"></script> </body> </html>
main.js内容如下:
//这个文件里面我们可能会写很多的js代码,并且现在我们的场景是这个文件里面的代码依赖一些module.js文件中的内容 import person from './module.js' //person这个名字随便起,因为export default把他们都放到了一个default变量上,任何变量名都可以接收到 console.log(person.name); console.log(person.f1());
module.js内容如下:
var person = { name:'张三', f1:function(){ alert(1); //弹出一个1对话框 } }; export default person;
但是我们通过浏览器打开这个index.html文件,你会发现报错了,浏览器不支持import等的写法,既然浏览器不支持这样的模块化,那怎么办呢,这就要借助我们的webpack工具了。
我说过webpack需要找到我们所有js文件(或者其他文件比如css文件)的一个入口文件,然后将所有的文件(jscss等)打包输出成为一个js文件,下面我们就来看看,如何通过webpack进行打包并实现模块化开发,记住一点,webpack工具必须在nodejs的环境下才能使用,这也是为什么我们要先下载nodejs。
看webpack的用法,我们先在pycharm的终端指令中执行一个webpack指令:
那么main.js和module.js文件内容不变,index.html文件中的引入改一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 只引入main.js,因为main.js用来作为所有js文件的入口文件 --> <!--<script src="main.js"></script>--> <!-- 直接引用我们通过webpack打包好的js文件 --> <script src="bundle.js"></script> </body> </html>
然后通过浏览器打开我们的index.html文件,效果就出来了,
具体的这个打包出来的文件内容,你暂时不需要理解,会用就可以了。
接下来我们使用一下export来抛出多个内容:
main.js的写法:
// export抛出后我们import来接收的方式:console.log(person,num); import {person,num} from './module.js' console.log(person,num) //但是这里要注意的就是,person和num要和别的文件抛出的变量名字相同 //一下接收所有的变量,*,但是*符号和我们的css有冲突,所以搞个别名,我这里起了个别名叫a import * as a from './module.js' console.log(a);
module.js的写法
var person = { name:'张三', f1:function(){ alert(1); } }; var num = 32; // export default person; export {person,num}; //通过export抛出多个变量,还能抛出函数,类等内容,我这里就没有写啦
index.html文件不用改:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 直接引用我们通过webpack打包好的js文件 --> <script src="bundle.js"></script> </body> </html>
别忘了,我们将js文件修改了,那么我们需要重新执行一下打包过程,webpack ./main.js ./bundle.js,中间的空格别忘了写,有朋友可能要说了,每次都手动打包一下吗?后面咱们就不用手动打包了,会有自动打包的方法,往后面学。
然后浏览器打开index.html文件看效果:在浏览器调试台的console的地方看
简单总结一下:
CMD:
抛出:module.exports = xxx; 引用:require()
ES6:
抛出:export export var num=10; export function add(){} var n = 100; var s = 'chao'; export {n,s} 引用: import * as a from './module.js' //as起别名 抛出:export default var person = { name:'张三', f1:function(){ alert(1); } }; export default person; 引用: import xxx from './module.js' xxx.name; xxx.f1();
大家现在为止是不是觉得很麻烦啊,每次都需要自行webpack打包一下,每次改动都要打包,那么脚手架提供给我们方便我们使用的框架,像这种 打包的事情就不需要自己做了,上面我们学习的export和import就是我们进行模块化开发的基础。
还记得我们上面自己下载的vue.js吗,这个文件是默认支持模块化开发的,下面我们看一个例子:
看我文件的目录结构:
其中main.js是我们要写的js代码,内容如下:
import Vue from './vue.js' //我们想要进行模块化开发,那么要做的就是将每个组件单独放到一个js文件中,而不是在这里在let一个组件了,当然我们可以写在这个js文件中,但是每个组件里面可能还嵌套着其他的组件并且有很多其他的结构和代码,如果都写在这一个js文件中,会很乱,很难维护,所以我们争取将这些组件进行解耦,所以你看我们又创建了一个App.js文件,文件名称我用首字母大写来写的,目的是让别人知道这是个组件 // let App = {}; // 引入我们写的App组件 import App from './App.js' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue实例对象</div> <App></App> </div> `, components:{ App, } });
App.js内容如下:
//定义一个组件 let App = { template: ` <div> 我是App组件 </div> ` }; //将组件抛出 export default App;
index.html文件内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <script src="bundle.js"></script> </body> </html>
然后我们执行webpack指令打包一下js文件,js入口文件就是我们的main.js,因为都是通过main.js引入的其他的js文件中的内容,首先在终端cd切换到我们的存放这些文件的文件夹下,然后执行指令:webpack ./main.js ./bundle.js,然后在index.html文件中引入一下我们打包出来的bundle.js文件。
然后通过浏览器打开我们的index.html文件,就看到了效果:
此时我们发现,我们每次改动js文件都要在终端输入webpack指令来打包一下,比较麻烦:
所以我们需要用一个简单的方式来执行webpack指令,打包我们的文件,那么我们就来学一下webpack的使用:
1.需要安装nodejs,这个我们已经安装好了
2.执行npm init --yes 指令(不加--yes,你看看是什么效果),默认会生成一个package.json文件(管理整个项目中的包资源,类似于我们python的django框架中的settings.py配置文件,配置某些东西用的)
首先,修改一下我们的目录结构,把项目文件夹名称改为英文的(不然会有一些编码错误问题):
然后我们在我们的项目目录下执行一下这个指令:其实这个指令也不要记了,后面我们会有一键生成这个package.json文件的操作。
package.json文件创建好了之后,我们就需要用它来管理我们的包了,首先我们要在我们的项目里面下载webpack包(之前webpack我们是安装在了全局,现在是搞到我们的项目里面,因为以后你的项目要打包上线的,现上不一定有你webpack的工具),执行指令npm i webpack@3.12.0 -D (-D,开发环境依赖,写上就行了),执行完这个指令之后,我们的项目目录下就多了这个node_modules文件夹,将来你这个项目打包上线,我们这个脚手架以及webpack工具需要的依赖就都在这个文件夹里面了,直接把它也打包给我们的线上环境。
然后看一下我们的package.json文件:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { //现在主要看这里,将来我们执行num run test,就会执行这个scripts属性里面的test属性对应的后面的脚本指令 "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
好,既然如此,我们想通过npm run dev,来执行webpack ./main.js ./bundle.js这个命令,写起来就简单多了,所以我们需要配置一下上面这个文件中的scripts里面的属性,看配置:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { // "test": "echo "Error: no test specified" && exit 1", 注意,你使用我这个配置的时候,把我写的这些注释全部去掉,不然没办法打包编译,会出错 "dev": "webpack ./main.js ./bundle.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //这是我们当前项目里面有的开发工具或者说是开发依赖,此时我们的工具有一个,叫做webpack,将来如果需要其他的工具,我们安装一下,这个地方就会显示出来,此时我们只安装了webpack,所以只显示了这个webpack的信息。 "webpack": "^3.12.0" } }
然后我们把刚才生成的bundle.js文件删掉,然后执行一下num run dev指令,那么这个指令就会执行我们在package.json中配置的scripts属性中的dev属性对应的值,也就是我们的那个webpack指令,那么同样会生成我们的出口文件bundle.js ,看效果:
好,我们通过npm run dev,代替了我们我们之前写的打包指令webpack ./main.js ./bundle.js,下面我们玩点webpack更高端的用法。
我们现在通过webpack想DIY一个脚手架,那么我们必须知道webpack的四个核心概念:
1.入口(entry):就是入口文件
2.出口(output):出口文件
3.loader :文件类型转换器
4.插件(plugins)
看webpack中文文档:
文档里面有关于上面四个核心概念的解释及应用。
入口和出口:
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
每个依赖项随即被处理,最后输出到称之为 bundles 的文件中,我们将在下一章节详细讨论这个过程。
可以通过在 webpack 配置中配置 entry
属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src
。
出口(output) 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist
。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output
字段,来配置这些处理过程。下面我们来搞一搞。
首先创建一个名为webpack.config.js的文件,必须是这个名字,这个文件就是用来配置我们上面说的webpack的四个核心概念(功能)的地方,那么先配置一下入口和出口,里面写下面的内容:
module.exports = { //入口配置 entry: { 'main':'./main.js', //入口文件路径 }, //出口配置 output:{ 'filename':'./bundle.js', //出口文件名称和路径 } };
写完这些东西后,我们的package.json中的scripts的地方也需要修改一下:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { // "dev": "webpack ./main.js ./bundle.js" //配置完webpack.config.js文件之后,这里就不这样写了,使用我这个内容的时候,别忘了将注释都去掉 "dev": "webpack" //执行npm run dev,那么就会找到webpack指令,并且这个webpack指令会找到webpack.config.js里面的配置,来执行打包指令 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
然后我们执行npm run dev指令,照样生成我们的bundle.js文件,打开我们的index.html文件照样能够实现页面效果。
但是又一想,每次修改了我们js文件里面的代码,比如修改了App.js组件里面的代码,我们都需要手动的执行一下这个npm run dev的打包指令,比较麻烦,我想让它自动执行怎么办,需要我们在webpack.config.js文件中配置一个watch属性,看配置:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路径 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名称和路径 }, //监听,实时监听代码的改动,一旦改动了,自动进行打包,那么就不要我们手动执行npm run dev指令了 watch: true, };
那么我们只需要再执行一次npm run dev打包指令,以后我们修改什么App.js等文件的代码之后,程序会自动会执行这个打包指令,来完成实时检测代码改动自动打包文件的效果,看操作:
首先执行一下npm run dev,你会发现终端的地方卡住了没有结束(因为在实时监听),然后通过浏览器打开index.html文件看效果:
然后,我们改一下App.js这个组件中的代码:
//定义一个组件 let App = { template: ` <div> 我是App组件aaaaa //之前这里是没有aaaaa的 </div> ` }; //将组件抛出 export default App;
之前我们改完代码,都需要再执行npm run dev打包指令,现在我们就不需要了,直接到浏览器上刷新我们的index.html看效果:
那么我们以后在我们的开发环境中,只要配置好了webpack.config.js文件,那么就执行一次打包指令,以后其他js文件中有任何的改动,都会自动打包,省去了我们手动执行打包指令的操作,方便了很多。
然后你可能又想,我们开发环境中的关于webpack的配置有可能和我们生产环境中使用这个项目的时候的配置不太一样,所以我们能不能单独给开发环境和生产环境都做一个配置呢,也就是准备两个webpack.config.js文件。
当然可以了啦,往下看,首先我们准备两个配置文件,名为webpack.dev.config.js(给开发环境准备的)和webpack.pro.config.js(给生产环境准备的),文件名字可以随便起昂,两个文件我都用的相同的配置,这里只是给大家演示一下。
webpack.dev.config.js内容如下:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路径 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名称和路径 }, //监听,实时监听代码的改动,一旦改动了,自动进行打包,那么就不要我们手动执行npm run dev指令了 watch: true, };
webpack.pro.config.js内容如下:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路径 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名称和路径 }, //监听,实时监听代码的改动,一旦改动了,自动进行打包,那么就不要我们手动执行npm run dev指令了 watch: true, };
好,我们的文件配置完了,那么我们需要修改一下package.json文件中关于webpack指令的配置:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": {
//修改了这里:dev指令我们在开发的时候用,build我们在生产环境中一次性打包的时候使用, "dev": "webpack --config ./webpack.dev.config.js", //执行npm run dev,会找这个路径的配置文件 "build": "webpack --config ./webpack.pro.config.js" //执行npm run build,会找这个路径的配置文件 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "webpack": "^3.12.0" } }
然后将我们的bundle.js文件和我们之前的webpack.config.js删了,然后我们再执行npm run dev看看效果,然后删除生成的bundle.js文件,然后再执行npm run build看看效果,就完成了我们给生产环境和开发环境的单独配置,生产环境我们执行npm run build,开发环境我们执行npm run dev。
然后我们将我们的index.html和打包编译好的bundle.js文件给服务器就可以拿去上线了。
再想,我们现在是不是只完成了js文件的打包啊,也就js文件的模块化开发,那我们的项目还需要css、图片等好多其他类型的文件呢,怎么搞,能不能通过webpack一起打包呢,可以的,往下看。
首先,我们创建一个index.css文件,内容如下;
body{ background-color: red; }
之前我们使用这个css文件是在我们的html文件的head标签中通过link标签的来引入,但是我们现在想,能不能将这个文件也通过打包,一起打包到我们的bundle.js文件中,然后让html文件来使用呢?看骚操作:
首先我们说我们执行webpack打包指令的时候,打包的文件都是从一个入口文件开始打包的,因为入口文件里面是开始引入其他文件的最开始的地方,也就是我们前面创建的那个main.js文件,那么好,我们需要在这个入口也引入一下这个css文件,
看main.js文件的内容:
import Vue from './vue.js' import App from './App.js' import './index.css' //关于文件的引入,直接就这样引入就可以了,上面两个的引入是因为我们引入的是文件中的变量,而这里我们直接引入的是文件,所以写法上略有不同 new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue实例对象</div> <App></App> </div> `, components:{ App, } });
那么此时我们还需要webpack的第三个核心概念的配置,loader转换器,其实对于webpack来说css文件也是一个模块。
loader介绍
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
好,首先我们需要下载配置一些对应文件类型的loader转换器,对于css文件的loader,我们需要下载这两个loader:css-loader和style-loader,css-loader是解析css文件的,而style-loader是解析style标签的,是将css文件中的css属性内容放到style标签中,并将这个style标签放到html文件的head标签的转换器,执行下面的指令:
npm i css-loader style-loader -D
执行指令之后,你看一下package.json文件,你就会发现这个package.json文件里面多了一些内容,这下你就该明白这个文件是干什么的了吧,看文件内容
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { //这里面多了这两个loader,及对应的版本信息 "css-loader": "^2.1.1", "style-loader": "^0.23.1", "webpack": "^3.12.0" } }
然后我们还需要在我们的webpack.dev.config.js和webpack.pro.config.js文件中做一些loader的相关配置,我就把一个文件的内容给大家列举出来吧,两个文件现在是一样的配置,看webpack.dev.config.js的内容:
module.exports = { //入口配置 entry: { 'main': './main.js', //入口文件路径 }, //出口配置 output: { 'filename': './bundle.js', //出口文件名称和路径 }, //监听,实时监听代码的改动,一旦改动了,自动进行打包,那么就不要我们手动执行npm run dev指令了 watch: true,
//这是关于webpack的核心指令中的loader的配置 module: { loaders: [ { test: /.css$/, //正则表达式 loader: 'style-loader!css-loader' //注意写法,!的作用是先执行后面的loader再执行前面的loader // 遇到后缀为.css的文件,webpack先用css-loader加载器去解析这个文件 // 最后计算完的css,将会使用style-loader生成一个内容为最终解析完的css代码的style标签,放到head标签里。 // webpack在打包过程中,遇到后缀为css的文件,就会使用style-loader和css-loader去加载这个文件。 } ] } };
修改了配置文件之后,我们还是需要执行npm run dev指令的,执行一下,然后浏览器打开index.html文件,你就看到css的效果了:
好,现在我们看到了body的背景颜色已经有了,那么如果是图片呢,我们怎么玩,接着看,首先我们先到网上下载一个图片放到本地,放到我们的项目目录下,或者用图片的网络地址
首先,我们先看看在哪里显示图片呢,我们现在就App.js这个插件,那我们就在这个插件里面显示图片把,所以我们现在修改一下App.js这个文件内容,看代码:
//引入本地图片文件,将图片内容作为一个变量引入,也就是给这个图片对象起了个变量名,后面我们要通过这个变量名来使用这个图片 import imgSrc from './meinv.jpg' //定义一个组件 let App = { data(){ return{ img:imgSrc,//使用数据属性来接收一下图片对象,然后在template中使用一下 } }, template: ` <div> 我是App组件aaaaa <div>
<!-- 执行了后面的npm run dev指令之后,默认会将我们的图片的名称用base64来编码,生成一个新的base64编码名字的图片文件 --> <img :src="img" alt="">
<img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554823994028&di=db5686fc98db2a66a0b4738645922f09&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F170614%2F1311511X7-2.jpg" alt="" width="200" height="200"> </div> </div> `, }; //将组件抛出 export default App;
然后下载安装一下对应的loader,执行下面的指令:
npm i url-loader file-loader -D
然后看一下package.json文件的内容,又自动增加了一下内容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", //多了它 "style-loader": "^0.23.1", "url-loader": "^1.1.2", //多了它 "webpack": "^3.12.0" } }
然后在我们的webpack.dev.config.js和webpack.pro.config.js中添加一下我们的图片loader相关配置
module.exports = { entry: { 'main': './main.js', }, output: { 'filename': './bundle.js', }, watch: true, module: { loaders: [ { test: /.css$/, loader: 'style-loader!css-loader' }, //配置图片的loader { test:/.(jpg|png|jpeg|gif|svg)$/, //正则 loader:'url-loader?limit=4000&name=pic/[name].[ext]' //对应的loader,limit是限制图片大小的,单位是bytes } ] } };
其中limit=4000
表示小于4000bytes
的图片将直接以base64的形式内联在代码中,可以减少一次http请求;name=pic/[name].[ext]
表示大于4000bytes
的图片将存入输出路径的pic/
文件夹下,并且图片命名格式不变。
然后执行npm run dev指令,然后我们用浏览器打开index.html文件看效果,两个图片都显示出来了:
如果说我们的html文件中需要引入其他的html文件怎么办呢,能不能也把其他的html通过webpack打包呢,也是可以的,接着看:
执行下面的指令:
npm i html-webpack-plugin --save-dev
然后在配置文件中配置loader
module.exports = { entry: { 'main': './main.js', //入口文件路径 }, output: { 'filename': './bundle.js', //出口文件名称和路径 }, watch: true, module: { loaders: [ { test: /.css$/, //正则表达式 loader: 'style-loader!css-loader' //注意写法,!的作用是先执行后面的loader再执行前面的loader }, //配置图片的loader { test:/.(jpg|png|jpeg|gif|svg)$/, //正则 loader:'url-loader?limit=4000' //对应的loader }, //配置html文件的 { test:/.less$/, loader:'style-loader!css-loader!less-loader' } ] } };
然后执行npm run dev指令就可以了,App.js中引入html文件也是用import,写法和引入图片的写法相同,就不多演示了。
好,到目前位置,我们已经可以完成整个页面中资源的加载和引用了,也就是完成了模块化的开发,但是现在发现我们的目录结构太乱了,我们来调整一下目录结构,现在的目录结构是这样的:
调整一下目录,我们在我们的项目目录下创建一个src文件夹存放我们的main.js、App.js、index.css等文件,也就是我们开发时写的内容文件,放到src文件夹中,然后我们的打包输出的bundle.js先给他删除,还有个vue.js文件,这是vue的功能文件,如果我们下载的这个vue.js文件的形式来使用的vue,那么这个vue.js文件一般也是放到src文件夹中的,如果我们是通过npm install vue的形式下载的,那么这个vue会在我们目录中的那个node_modules文件夹中,图片文件比如我们的meinv.jpg文件放到一个static文件夹下面的pic文件夹下面,然后调整后的目录结构是下面这样的:
图片位置发生变化了,别忘了改我们的引入图片的地方的代码,我们在App.js这个模块中引入的图片,所以我们修改一下App.js文件,看代码:
//引入图片文件,将图片内容作为一个变量引入,也就是给这个图片对象起了个变量名,后面我们要通过这个变量名来使用这个图片 import imgSrc from '../static/pic/meinv.jpg' //修改为这个路径 // let imgSrc = require('../meinv.jpg'); 这样写也行 console.log(imgSrc); //定义一个组件 let App = { data(){ return{ img:imgSrc,//使用数据属性来接收一下图片对象,然后在template中使用一下 } }, template: ` <div> 我是App组件aaaaa <div> <img :src="img" alt=""> <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554823994028&di=db5686fc98db2a66a0b4738645922f09&imgtype=0&src=http%3A%2F%2Fpic.feizl.com%2Fupload%2Fallimg%2F170614%2F1311511X7-2.jpg" alt="" width="200" height="200"> </div> </div> `, }; //将组件抛出 export default App;
我们将来想将打包的那个bundle.js文件保存在一个叫做dist的文件夹下怎么搞呢,需要配置一下我们的webpack.dev.config.js和webpack.pro.config.js文件,看配置后的内容:
//引入nodejs的path模块,解析路径用的 const path = require('path'); module.exports = { entry: { 'main': './src/main.js', //main的路径变了,别忘了修改 }, //出口配置也需要改一下 output: { // 'filename': './bundle.js', //出口文件名称和路径 //文件目录调整之后,我们的出口文件配置也需要该一下,意思是将我们打包之后的那个js文件保存到一个我们设定的位置,看下面的写法 path:path.resolve('./dist'),//相对转绝对,这个path相当于python中的os.path功能模块,它是nodejs的模块,我们需要在上面引入一下这个模块 filename:'bundle.js' //出口文件名称,现在这个配置的意思是在我们的项目目录下生成一个dist文件夹,并且将所有打包的模块(也就是文件),以bundle.js文件名称保存在这个dist文件夹中 }, watch: true, module: { loaders: [ { test: /.css$/, loader: 'style-loader!css-loader' }, { test:/.(jpg|png|jpeg|gif|svg)$/, loader:'url-loader?limit=4000' }, { test:/.less$/, loader:'style-loader!css-loader!less-loader' } ] } };
然后我们执行npm run dev指令,看效果,目录结构中就多了dist文件夹,文件夹中就有了我们打包的bundle.js文件,还有我们配置的图片,也就是这个dist文件夹是给我们将来上线的服务器用的,其中图片是我们需要的,也就帮你一起打包到了这个dist文件夹里面,将来我们在服务器上做一下相关路径配置就可以了,好,看看我们的目录结构:
我们说这个dist文件夹是将来给我们的服务器的,那么我们html文件比如我们的index.html文件是不是也要发给服务器啊,当然啦,所以我们也希望我们的html文件资源也会自动打包到我们的dist文件夹下,那么此时我们就需要在进行一些配置,往下看:
先下载一个loader,执行下面的指令:
npm i html-webpack-plugin --save-dev #--save-dev就是 -D的意思
然后我们看一下package.json文件的变化:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", //多了一个它 "style-loader": "^0.23.1", "url-loader": "^1.1.2", "webpack": "^3.12.0" } }
执行完上面的指令,此时就需要用我们webpack中的另外一个核心内容,插件plugins。
插件plugins
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建它的一个实例。
好,我们来配置一下我们的那两个配置文件,webpack.dev.config.js和webpack.pro.config.js,内容如下:
const path = require('path'); //引入我们下载好的html文件的插件包 const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { 'main': './src/main.js', }, output: { path:path.resolve('./dist'), filename:'bundle.js' }, watch: true, module: { loaders: [ { test: /.css$/, loader: 'style-loader!css-loader' }, { test:/.(jpg|png|jpeg|gif|svg)$/, loader:'url-loader?limit=4000&name=pic/[name].[ext]' / }, { test:/.less$/, loader:'style-loader!css-loader!less-loader' }, ] }, //配置插件 plugins:[ new HtmlWebpackPlugin({ //插件的执行运行与元素索引有关 template:'./index.html', //参照物,也就是将来打包的时候,打包哪个html文件啊,要给他一个参照 }) ] };
然后我们执行npm run dev指令,然后看我们的目录结构
那么打开这个dist文件夹下的index.html文件看一下里面的内容:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"></div> <script src="./dist/bundle.js"></script>
<!-- 自动帮我们在index.html为你暗中添加了下面这个script标签,并引入了bundle.js,所以以后我们打包html文件的时候,之前的html里面的用来引入bundle.js的script就可以删除了,也就是上面这个我们之前写的script标签 --> <script type="text/javascript" src="bundle.js"></script></body> </html>
用浏览器打开这个index.html文件,看看效果,完全ok:
其实最后我们不管怎么开发,开发完成之后都要交给运维部署到线上,而我们给运维人员的东西就这个打包好的dist文件夹,里面有我们开发好的项目的所有资源。
其实我们要进行组件化开发,我们现在还没有做完,我们继续完善昂,首先我们src里面都是我们开发的内容,其中我们的App组件应该是.vue结尾的文件,然后进行单文件引入功能,如果想进行单文件引入,那么需要两个工具,一个是vue-loader,一个是vue-template-compiler,那我们来下载一下,执行下面的指令: vue-loader就是用来解析我们的.vue结尾的文件的,vue-template-compiler是用来编译.vue结尾的文件中的template模板的。
npm install vue-loader@14.1.1 vue-template-compiler@2.5.16 -D #注意这个vue-template-compiler要和你的vue版本一致
那么将来我们进行组件化开发,写.vue结尾的文件组件时的结构是这样的,看代码:
//组件的模板结构 <template> <div> {{ text }} </div> </template> //组件的业务逻辑 <script> export default { data(){ return { text:'hello Single file' } } } </script> //组件的样式 <style> body{ background-color: green; } </style>
好,上面的命令执行完之后,我们以后在写组件就用.vue结尾的文件来写,那么我们将我们的App.js文件改为App.vue文件,然后App.vue文件里面的内容就要按照上面的这种模板样式来写了。
然后我们还需要在我们的webpack.dev.config.js中配置一些内容:
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: { 'main': './src/main.js', }, output: { path: path.resolve('./dist'), filename: 'bundle.js' }, watch: true, module: { loaders: [ { test: /.css$/, loader: 'style-loader!css-loader' }, { test: /.(jpg|png|jpeg|gif|svg)$/, loader: 'url-loader?limit=4000&name=pic/[name].[ext]' }, { test: /.less$/, loader: 'style-loader!css-loader!less-loader' }, // 处理Vue文件,注意,就多了下面这个loader的配置 { test: /.vue$/, loader: 'vue-loader' } ] }, plugins: [ new HtmlWebpackPlugin({ template: './index.html', }) ] };
然后我们在App.vue文件中写上下面的内容,看代码:
//template是个标签,我们下载的那个vue-template-compiler解析提供的,也是通过它来编译这个标签内容,而我们下载的安格vue-loader是解析vue文件的 <template> <div> 我是Jedan </div> </template> <script> export default { name:'App',//可以给这个组件起个名字 data(){ return{ } }, methods:{ }, //之前组件里面我们会写一个template的属性,现在不用了,因为我们下载的工具自动帮我们创建了一个template模板标签,我们在上面直接使用就可以了 //template:``, } </script> <style> </style>
别忘了我们之前是在main.js里面引入的App.js,现在我们的文件叫App.vue了,你引入的地方也要改,看main.js的代码:
import Vue from './vue.js' import App from './App.vue' //这个文件的名字别忘了改 import './index.css' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue实例对象</div> <App></App> </div> `, components:{ App, } });
然后执行一下npm run dev,然后通过浏览器打开index.html文件看效果:完美:
那么将来关于我们写的所有组件,我们都可以放到src文件夹下的一个叫做components的文件夹中,看目录结构
然后我们基于这种结构做一个单页面应用:
首先,做单页面应用会用到我们的vue-router,所以我们需要给我们的项目下载一下vue-router,我们也可以将我们之前用过的vue-router.js文件拿过来用,但是以后不建议下载文件来使用了,我们都直接通过指令给我们的项目来现在这些功能,包括vue.js,所以我们将vue.js文件删了吧,我们通过npm下载,来执行下面指令,注意执行下载项目中需要的工具的时候,在终端下载的时候,我们要确保我们在自己项目目录下执行的指令。
npm install vue@2.5.16 vue-router -S #下载vue(注意版本要和上面的vue-template-compiler版本一致)和vue-router,-S的是项目环境依赖,而之前我们下载的webpack会用到的一些工具,是开发环境依赖,没有那些东西,你的项目照样能跑起来,只不过不太方便,但是没有vue和vue-router,你的单页面应用是跑不起来的
执行上面的指令之后,我们看一下package.json文件的内容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack --config ./webpack.dev.config.js", "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.16", "webpack": "^3.12.0" }, "dependencies": { //多了它 "vue": "^2.6.10", "vue-router": "^3.0.3" } }
然后别忘了,我们之前main.js里面是导入vue.js文件来使用vue功能的,但是现在我们已经把vue.js删了,并且是通过npm下载的vue,那么引用vue的时候,引用方式要改一改,看mian.js的内容:
// 不再是引用文件的形式引用vue了 // import Vue from './vue.js' import Vue from 'vue' //而是直接这样引用,像引用内置模块的形式 import App from './App.vue' import './index.css' new Vue({ el:'#app', data(){ return{ } }, template: ` <div> <div>我是Vue实例对象</div> <App></App> </div> `, components:{ App, } });
既然我们要做单页面应用使用main.js,那么我们把main.js的代码改一改,引用vue-router,看代码:
// 不再是引用文件的形式引用vue了 // import Vue from './vue.js' import Vue from 'vue' //而是直接这样引用,像引用内置模块的形式 import App from './App.vue' import './index.css' //引入vue-router import VueRouter from 'vue-router' //注意,引入我们下载好的包的时候,from后面一定是包的名字,前面import后面的名字你可以随便起,尽量不要和你的包名字相同,现在的意思是我的vue-router功能包被引入进来了,并且起了个名字叫做VueRouter //声明组件(引入组件) import Home from './components/Home/Home.vue' import Course from './components/Course/Course.vue' //别忘了,基于模块化开发的时候,我们还要执行一下Vue.use(VueRouter) Vue.use(VueRouter); //接下来就可以写我们的路由了,创建路由对象,别忘了将这个路由对象写到vue实例中 const router = new VueRouter({ //配置路由信息 routes:[ { path:'/', name:'Home', component:Home //对应Home组件,而我们的Home组件在src文件夹下的components文件夹的Home文件夹下面,所以我们在上面就需要引入一下我们的Home组件了 }, { path:'/course', name:'Course', component:Course //对应Home组件,而我们的Home组件在src文件夹下的components文件夹的Home文件夹下面,所以我们在上面就需要引入一下我们的Home组件了 } ] }); new Vue({ el:'#app', router,//给vue实例绑定路由对象 data(){ return{ } }, //使用render方法来渲染,因为后面咱们要下载一个个webpack-dev-server,高级一些的webpack打包工具,但是不太支持下面原来的写法,所以我换成了render方法 render:c=>c(App), // template: // ` // <div> // <div>我是Vue实例对象</div> // <App></App> // </div> // `, // components:{ // App, // } });
然后我们的App.vue里面就可以使用我们的router-link和router-view了,看代码:
//template是个标签,我们下载的那个vue-template-compiler解析提供的,也是通过它来编译这个标签内容,而我们下载的安格vue-loader是解析vue文件的 <template> <div> 我是Jedan <router-link :to="{name:'Home'}">首页</router-link> <router-link :to="{name:'Course'}">课程页</router-link> <router-view></router-view> </div> </template> <script> export default { name:'App',//可以给这个组件起个名字 data(){ return{ } }, methods:{ }, //之前组件里面我们会写一个template的属性,现在不用了,因为我们下载的工具自动帮我们创建了一个template模板标签,我们在上面直接使用就可以了 //template:``, } </script> <style> </style>
然后我们写一下我们的Home组件和Course组件:
Home.vue代码如下:
<template> <div> 这里是首页 </div> </template> <script> export default { data(){ return{ } } } </script>
Course.vue代码如下:
<template> <div> 这里是课程页 </div> </template> <script> export default { data(){ return{ } } } </script>
组件都写好了之后,我们再学一个新东西,叫做webpack-dev-server,比webpack更高级一些的打包工具,是基于服务器的,他能够在前端起服务器,来服务端的形式打开我们的vue项目,这个工具可以自动打开浏览器、热更新、自动刷新等功能,下面我们来下载一下,执行指令:
npm install webpack-dev-server --save-dev
这个webpack-dev-server模块的常用配置参数如下:
常用配置参数 --open 自动打开浏览器 --hot 热更新 ,不在刷新的情况下替换 css样式 --inline 自动刷新 --port 9999 指定端口 --process 显示编译进度
下载好了以后,我们来使用一下,还记得我们之前在package.json文件中如何配置我们的webpack指令的吗,就是执行那个npm run dev,就会执行webpack打包的那个地方,我们需要改一改写法了,我下面只修改一下开发环境的那个webpack指令,看packag.json的内容:
{ "name": "03module_deep", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "dev": "webpack-dev-server --open --hot --inline --config ./webpack.dev.config.js", //就是这个地方,之前是用的webpack --config ./webpack.dev.config.js,现在用我们的webpack-dev-server了,使用webpack-dev-server,不会帮你生成dist文件夹,因为你是开发环境,webpack会帮你生成 "build": "webpack --config ./webpack.pro.config.js" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^2.1.1", "file-loader": "^3.0.1", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", "url-loader": "^1.1.2", "vue-loader": "^14.1.1", "vue-template-compiler": "^2.5.16", "webpack": "^3.12.0", "webpack-dev-server": "^2.11.5" }, "dependencies": { "vue": "^2.5.16", "vue-router": "^3.0.3" } }
好,配置好以后,我们把之前webpack打包出来的那个dist文件夹删除,然后再执行npm run dev,就会通过webpack-dev-server来打包,并且它自动会帮我们打开页面
然后你就看到了页面效果,看浏览器的地址栏,是一个服务端的形式:
然后你在组件的模板中随便改写内容,页面会自动刷新,不需要你手动刷新了。
好,到这里,我们的DIY脚手架就算是搭建完成了,那么我们说一下我们的目录结构,其实我们将来用脚手架,dist文件夹是没有的,我们自己开发完之后,通过打包才生成的,好了,感受了一下搭建脚手架的过程,DIY脚手架大家不用会,理解过程就行了,我们还是学习怎么用人家vue给我们提供的脚手架吧,继续下面的学习~~
首先我们对比一下vue-cli2x版本和vue-cli3x版本的一些区别,现在2x版本的:
安装
npm install -g vue-cli
用法
$ vue init < template-name模块名 > < project-name项目名 >
创建一个例子:
$ vue init webpack my-project
目前除了webpack之外,其他的一些可用模块:
目前可用的模块包括: - [webpack](https://github.com/vuejs-templates/webpack) - 一个功能齐全的Webpack + vue-loader设置,具有热重载,linting,测试和css提取功能。 - [webpack-simple](https://github.com/vuejs-templates/webpack-simple) - 一个简单的Webpack + vue-loader设置,用于快速原型设计。
目前主要用的上面两个,webpack-simple简单一些,webpack功能更加齐全
- [browserify](https://github.com/vuejs-templates/browserify) -全功能Browserify + vueify设置用热重装载,linting&单元测试。 - browserify [-simple](https://github.com/vuejs-templates/browserify-simple) - 一个简单的Browserify + vueify设置,用于快速原型设计。 - [pwa](https://github.com/vuejs-templates/pwa) - 基于webpack模板的vue-cli的PWA模板,主要应用于移动端的开发 - [simple](https://github.com/vuejs-templates/simple) - 单个HTML文件中最简单的Vue设置
然后我们下用webpack-simple来创建一个项目,在你的目录下执行下面的指令:
然后我们的目录下就有了这个项目的文件,我们看一下目录结构:
然后说几个目录下的其他文件是干什么的,作为了解吧:
那么node_modules这些依赖包怎么下载呢,我们看一下我们都少了哪些依赖包和工具啊,看一下自动生成的package-lock.json文件:
好,我们切换到我们的项目目录下,然后执行一个npm install指令,就会自动帮我们下载项目的依赖了:
然后执行指令npm run dev指令,你就会发现,页面自动打开了:
页面效果:
好,一个vue的项目就成型了,就像我们学python的时候,通过django创建一个项目,直接一运行,看到django给你们提供的首页似的那个感觉。
然后我们看一下执行npm run dev指令后的一个项目执行顺序:
如果将来我们项目写完了,想打包发给上线人员,那么我们执行npm run build指令,就会生成打包好的dist文件夹,里面有咱们的图片文件,js文件等,如果你安装了咱们说的那个html文件打包插件,那么这个dist文件夹里面也有咱们的html文件。
然后看一下webpack.config.js里面的内容:
var path = require('path') //引入路径解析墓模块 var webpack = require('webpack') //引入webpack module.exports = { entry: './src/main.js', //webpack打包时的入口文件,将来我们就先从这个文件开始玩咱们的项目 output: { //出口文件配置 path: path.resolve(__dirname, './dist'), //__dirname是项目的绝对路径,resolve将他俩拼接了一下 publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ //可以写成loaders { test: /.css$/, //打包css文件用的 use: [ 'vue-style-loader', 'css-loader' ], }, { test: /.vue$/, //打包vue文件用的 loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /.js$/, //打包js文件用的 loader: 'babel-loader', exclude: /node_modules/ }, { test: /.(png|jpg|gif|svg)$/, //打包图片文件用的 loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { //别名 'vue$': 'vue/dist/vue.esm.js' //esm是esmodule }, extensions: ['*', '.js', '.vue', '.json'] //扩展,凡是后缀名有他们几个的文件,后缀名可以忽略不写 }, //在下面的那些配置咱就不多看了 devServer: { historyApiFallback: true, noInfo: true, overlay: true }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) }
项目的入口是main.js文件,打开看看,你会发现你很熟悉:
import Vue from 'vue' //引入vue import App from './App.vue' //引入App组件,也是我们创建项目时自动给你生成好的,有import,那么App.vue文件中肯定有export new Vue({ el: '#app', render: h => h(App) //挂载,使用APP组件,和template+components一样,但是webpack-dev-server支持这个方法,不支持template, })
然后看一下App.vue组件中的内容:就是咱们创建完项目,下载完依赖,然后执行npm run dev指令之后你看到的那个页面里面的内容:
<template>//还记得.vue结尾的文件的模板吗,template script style三个标签,template是组件中页面渲染的标签,script标签中抛出组件,style标签中写css样式,以后,我们直接把下面这些内容清空,也我们的单页面应用就可以了 <div id="app"> <img src="./assets/logo.png"> <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> </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> </div> </template> <script> export default { name: 'app', data () { return { msg: 'Welcome to Your Vue.js App' } } } </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; } h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
好,接下来我们就要玩我们的单页面应用了,还记得单页面应用需要什么吗,vue-router,首先下载,执行下面的指令:
npm i vue-router -S 注意,别用-g,而是用-S,因为-g是下载到全局了,全局的意思就是我们下载安装vue的咱们那个电脑目录里面了,而-S是下载到我们当前项目的依赖中了,不一样的昂
然后我们需要写我们路由配置了,在哪里配置呢,还记得吗,就是我们的main.js文件,并且之前我们写路由配置信息的时候,都是写在了这个文件里面,那么随着我们项目越来越大,你会发现路由信息会越来越多,那么为了方便管理,我们应该将路由信息单独的放到一个文件里面去,并且,我们路由肯定都对应着组件,也就是说将来的组件也会越来越多,所以组件我们也单独的存放起来,并且我们以后开发,都只关注src文件夹里面的内容,所以路由和组件都放到src文件夹里面,看下面的目录结构:
目录结构搞好了,我们就开始开发了,看代码:
main.js代码如下:
import Vue from 'vue' import App from './App.vue' //导入router路由配置信息,因为我们要在vue实例对象里面挂载一下,还记得吗 // import router from './router/router-config' //还记得webpack.config.js里面有个配置说,凡是.js等结尾的文件,可以不写后缀名吗,写不写都可以 import router from './router/router-config.js' //有import,就肯定要在router-config.js文件中有个export抛出router对象 new Vue({ el: '#app', router,//挂载router对象 render: h => h(App) });
router-config.js路由配置信息代码如下:
//引用vue,因为vue-router依赖于vue import Vue from 'vue' import VueRouter from 'vue-router' //引入对应的组件 import Home from '../components/Home/Home' import Course from '../components/Course/Course' //给Vue加载一下VueRouter,其实类似于继承一下 Vue.use(VueRouter); const router = new VueRouter({ routes:[ { path:'/home', name:'Home', component:Home, }, { path:'/course', name:'Course', component:Course, } ] }); export default router;
接下来是对应组件,Home.vue组件代码如下:
<template> <div> 这是首页 </div> </template> <script> export default { //抛出组件,供别的地方引用 name:'Home', // 这个name在做全局组件的时候比较有用,还记得全局组件怎么创建吗?vue.component('Home',{}),第一个参数就是组件名称,其他组件使用这个全局组建的时候就靠的这个组件名称,而我们这里写的name,就可以起到这个作用,将来别人挂载你的组件的时候,就用它 data(){ return{ } } } </script> <style> </style>
Course.vue组件代码如下:
<template> <div> 这是课程页 </div> </template> <script> export default { name:'Course', data(){ return{ } } } </script> <style> </style>
然后是App.vue组件中要引入这两个组件了,看App.vue文件的代码如下:
<template> <div id="app"> <!-- 通过路由对应路径,自动生成a标签,href属性指向对应的路径,还记得吗? --> <router-link :to="{name:'Home'}">首页</router-link> <router-link :to="{name:'Course'}">课程页</router-link> <!-- 组件出口 --> <router-view></router-view> </div> </template> <script> export default { name: 'App', data () { return { msg: 'Jaden' } } } </script> <style> </style>
然后执行npm run dev,直接就看到了我们页面效果,点击首页就看到了首页内容,点击课程页,就看到了课程页内容:
齐活,我们的项目就写完了,哈哈
接下来我们玩一下CSS样式,学一个scoped属性。
我给App组件还有Home和Course组件都加了个h2标签,然后App组件的style标签中给h2标签设置了样式,大家看图:
看效果:
由于App组件算是我们整个页面的大组件,全局组件,所以如果直接按照上面的方式来设置样式,那么App组件中嵌套的其他组件也会应用上这个样式,并且如果我们在其他组件里面给h2标签设置了其他的样式,那么只要点击加载过这个组件,那么全局的所有的组件的h2标签的样式就会编程这个样式,所以很不好控制,看图:
看效果,首先刚开始的时候,我们页面加载了App组件,效果是这样的:
但是只要点击了Course组件,看效果:
你会发现,这样很难控制我们每个组件的自己的样式,这怎么搞呢,style标签的scoped属性,注入的意思。
看效果:
这就是scoped属性的作用。
以上是我们使用webpack-simple模块创建的项目的玩法,但实际工作中我们用的一般都是webpack模块,复杂一些的模块,功能也多,而且文件夹都给咱们分好了。但是学了webpack-simple之后,webpack对你来说也简单了,接下来我们学一下wepack模块创建项目的玩法。
首先我们通过webpack来创建一个项目,在IDE终端执行下面的指令
vue init webpack 05-webpack-project(项目名称)
然后终端会给咱们一些提示输入信息的选项,看下图:
除了上面这些提示,还有一些提示,接着看:
这里项目的依赖,我选择的是npm下载,回车,然后等一会就下载完了,下载完之后还有提示:
然后按照提示,我们cd到我们的项目目录下,执行npm run dev指令,就启动我们的项目了,在终端你会看到这个:
然后拿着这个ip地址和端口,到我们的浏览器上访问一下,就看到我们的页面了:
下面我们看一下新生成的项目的目录结构:
然后我们执行npm run dev会执行什么呢,还记得吗,是不是找package.json里面的scripts属性里面的dev对应的那个指令啊:
然后看一下这个webpack.dev.conf.js文件:
然后看一下这个webpack.base.conf.js文件,就看到了我们熟悉的内容:
那么这些配置文件咱们其实都不用管,我们只需要专注的玩我们src文件夹里面的内容就可以了,开发者只需要关心src这个文件夹,那么我们先从入口文件main.js看看里面的内容:
然后你会发现,其实这个main.js文件你都不用管了,我们只需要在我们的src文件夹里面的App.vue组件写一写,然后把components文件下的组件写一写、分一分,然后把router文件夹下的index.js路由配置信息改一改,就完事儿了。下面我就简单的写一个单页面应用,大家感受一下吧,先看目录结构:
然后直接上代码:
main.js文件内容如下:
// 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' import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
router文件夹下的index.js路由配置信息内容如下:
import Vue from 'vue' import Router from 'vue-router' // import HelloWorld from '@/components/HelloWorld' //@还记得吗,表示src文件夹的根路径 //引入组件 import Course from '@/components/Course/Course' import Home from '@/components/Home/Home' // console.log(Course); //给Vue添加vue-router功能,使用别人提供的功能都要用Vue来use一下 Vue.use(Router) //创建路由对象 export default new Router({ mode:'history', //去掉浏览器地址栏的#号 //配置路由信息 routes: [ { path: '/', // redirect:'Home' //直接跳转Home名字对应的path路径上 redirect:'/home' }, { path: '/home', name: 'Home', component: Home }, { path: '/course', name: 'Course', component: Course } ] })
App.vue组件文件的内容如下:
<template> <div id="app"> <router-link :to="{name:'Home'}">首页</router-link> <!--<router-link :to="{name:'Home',query:{user_id:2}}">首页</router-link>--> <!--query:{user_id:2} 是query参数,拼接到路径上是这样的/home?user_id=2,这也是动态路由的一种,之前我们说了一个param参数,这个param参数是/home/2,拼接在路径上的--> <router-link :to="{name:'Course'}">免费课程</router-link> <!-- 组件出口 --> <router-view></router-view> </div> </template> <script> export default { name: 'App', data(){ return{ } }, } </script> <style> </style>
Home.vue组件内容如下:
<template> <div> 这是Home页面 </div> </template> <script> export default { name: 'Home', data(){ return{ } }, } </script> <style> </style>
Course.vue组件内容如下:
<template> <div> 这是Course页面 </div> </template> <script> export default { name: 'Course', data(){ return{ } }, } </script> <style> </style>
把这几个文件写好,那么我们执行npm run dev,就启动我们的项目了,按照ip地址和端口的提示,我们在浏览器上访问一下这个地址,就看到我们的页面应用了:
效果:
非常好,单页面应用我们就写完了,剩下的就是完善网站的功能和美化页面效果了。
简单总结一下:
1. webpack:
entry:整个项目的程序入口(一般是main.js或index.js)
output:输出的出口
loader:加载器、转化器和对es6代码的解析(这个解析用的是babel编译器,大家可以去看看babel),有些低版本的浏览器不能识别es6的代码,为了做这种兼容性,也就是为了让低版本的浏览器能够识别es6的代码,前端在webpack一般用这个babel loader去处理,去解析,看babel官网。
再比如还有什么css-loader解析css文件和style-loader将css代码添加一个style标签,插入到header标签中,并且支持模块化。还有一些图片引入等用的是url-loader,还有很多其他的loader,他的作用就是对我们的静态文件资源做支持的。
plugins:插件,比如那个html-webpack-plugin,打包html文件用的,还有一些插件做代码压缩的(那个丑陋js插件),还有分割js文件的插件,比如js文件比较大的时候,会一部分一部分的加载,还有我们知道咱们现在是组件化开发,每个组件都有自己的js文件,而目前为止我们都是将所有的js文件全部打包了,每次使用都是将所有的js代码全部加载到浏览器上,但是如果做个非常大型的项目,我们应该考虑,点击一个页面,这个页面对应的那些组件的js代码才加载到页面上,也就是将js代码或者js文件分开,这个就稍微麻烦一些了,大家可以去研究一下webpack。前端做的工作还有就是项目上线之前,对整个项目进行优化。webpack内容很多,如果想做一个专业的前端,需要好好学习和关注webpack,还有其他的前端社区,最新的技术都是需要学习的,不光前端如此,后端也是如此,机会是给有准备的人的。
2.使用vue-cli
a.电脑上(linux,windows等)需要安装nodejs,使用npm包管理器
b.npm下载脚手架,如果你下载的是最新的3.x版本的,那么别忘了拉一下2.x版本的桥接工具,为了兼容2.x版本,目前公司里用的比较多的还是2.x版本的。3.x版本和2.x版本创建项目的命令也是不同的,大家自行看看吧。
安装3.x版本:npm install -g @vue/cli ,拉取2.x版本的:npm install -g @vue/cli-init
创建项目:vue init webpack my-project
执行指令的时候先看清楚当前终端的根目录,如果不是我们的项目目录,一定要先切换到项目目录下,然后执行npm install 来下载项目的所有依赖包
执行项目:npm run dev(当然dev这个名字,自己可以配置,在package.json文件中的scripts属性中改)
3. .vue组件文件,结合我们学的基础,怎么玩,看代码:
<template> <!--当前组件的结构--> <div> <h2>{{ msg }}</h2> <!-- 模板语法,渲染组件的数据属性 --> <!-- 指令系统 v-html、v-for(别忘了:key,不然我们如果更改了组件中的一些内容,重新排版页面的时候,可能会出现问题,出现混乱,改的数据跑到了别的地方), --> <!--<span(或者组件标签) v-for="(value,index) in nameList" :key="index或者value.id"></span>--> <!-- v-bind(:), v-on(@),v-model双向数据绑定 --> <!-- 使用element-ui组件,作为我们组件的子组件,里面的数据通过父子组件,平行组件传值 --> </div> </template> <script> //当前组件的业务逻辑 export default { name: 'Test', data(){ return{ msg:'Jaden', nameList:[] } }, methods:{ }, //计算属性 computed:{ }, //还有各种钩子函数 created(){ }, watch:{}, //过滤器 filter:{ myTime:function (val) { } } } </script> <style scoped> /*当前组件的样式*/ </style>
补充一点,怎们将我们的组件做成一个全局组件呢?那就想一下全局组件怎么创建,是不是vue.component('组件名',{}),好,要使用vue,那么哪个文件引用的vue,是不是我们的main.js啊,所以我们在main.js里面就可以将我们的组件做成全局组件,大概的写法看下面的代码,main.js的:
// 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' import router from './router' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); import '../static/global/index.css' Vue.config.productionTip = false //将我们之前的Home组件做成一个全局组件 // import HomeContent from './components/Home/Home' // Vue.component('HomeContent.name',HomeContent); /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
element-ui是饿了么开发的一套前端框架,类似于bootstrap,但是他是协同vue进行开发的,它提供好了很多vue能够直接下载使用的组件,组件里面封装好了html、css、js等代码,拿来就能用,就能得到对应的效果。
好,我们进入官网看一下http://element-cn.eleme.io/#/zh-CN/,其中我们就看组件的部分:
首先下载安装,推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。执行下面的指令来安装:
npm i element-ui -S
然后怎么玩呢,看下图:
那么好,知道了步骤,我们就来项目里面搞一搞,首先终端执行上面的下载安装命令,安装完成后我们去配置我们的main.js,看代码:
// 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' import router from './router' //引入element-ui import ElementUI from 'element-ui'; //引入element-ui的css样式,这个需要单独引入 import 'element-ui/lib/theme-chalk/index.css'; //给Vue添加一下这个工具 Vue.use(ElementUI); Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
然后使用一下element-ui的组件:
然后在我们的App.vue组件中使用一下,看App.vue文件的代码:
<template> <div id="app"> <el-container> <!-- 这是个组件,全局组件,是当前App组件的子组件,这些element-ui里面的这些组件肯定都是些全局组件,我们引入了element-ui,并且vue.use()了一下,将像vue.component(element-ui组件,{}),这些子组件里面的数据就是我们父子组件传值传进去的,是说对不对 --> <el-header> <!-- 将组件入口写到了这header里面 --> <router-link :to="{name:'Home'}">首页</router-link> <router-link :to="{name:'Course'}">免费课程</router-link> </el-header> <el-main> <!-- 组件出口写到了el-main里面 --> <router-view></router-view> </el-main> </el-container> </div> </template> <script> export default { name: 'App', data() { return {} }, } </script> <style> </style>
然后执行npm run dev指令,启动我们的项目,然后在页面上看效果:
之前的页面效果是这样的:
加上element-ui的布局容器之后,效果是这样的:
好,那么我们再添加一些css样式:
我们在static文件夹下创建一个文件夹存放css样式,先创建一个全局的css样式文件夹,里面创建一个index.css文件,来写全局的一些css样式,看目录结构:
然后我们在main.js里面引入一下这个全局css样式,看main.js的内容:
// 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' import router from './router' //引入element-ui import ElementUI from 'element-ui'; //引入element-ui的css样式,这个需要单独引入 import 'element-ui/lib/theme-chalk/index.css'; //给Vue添加一下这个工具 Vue.use(ElementUI);
//引入我们自己创建的css样式文件 import '../static/global/index.css' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
看页面效果:这样我们的css样式就有了。
再举一个例子吧,比如说来一个时间选择器:
然后比如说我们在Home组件中使用一下,看一下Home.vue文件内容:
<template> <div> 这是Home页面 <el-time-select v-model="value1" :picker-options="{ start: '08:30', step: '00:15', end: '18:30' }" placeholder="选择时间"> </el-time-select> </div> </template> <script> export default { name: 'Home', data() { return { value1:'', //时间选择器,这里别忘了return一个value1来对应上面的v-model } }, } </script> <style> </style>
效果就出来了:
然后大家自己过一下element-ui都给咱们提供了哪些组件就行啦,以后直接就用。
我们再说一个组件怎么用吧,就是element-ui提供的Carousel走马灯(轮播图)
看这个走马灯怎么玩:
好,到这里我们就说的差不多了,大家完成一个内容吧:
然后主要写免费课的那个组件,也就是点击免费课的那个页面:
行,完成这些内容吧!