返回目录
返回目录
什么是组件化?
Vue的组件思想
组件化思想的应用:
返回目录
步骤:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<!--3.使用组件-->
<div id="app"><cpn></cpn><cpn></cpn><cpn></cpn><cpn></cpn>
</div><script src="../js/vue.js"></script>
<script>//ES6用``可以代替字符串并不需要+拼接来换行写// 1.创建组件构造器对象const cpnC = d({template:`<div><h2>我是标题</h2><p>我是内容,哈哈哈哈</p><p>我是内容,呵呵呵呵</p></div>`});//注册组件Vueponent('cpn',cpnC)const app = new Vue({el: '#app',data: {message: '你好啊'}});
</script>
</body>
</html>
注册组件步骤解析:
Vueponent():
组件必须挂载在某个Vue实例下,否则它不会生效
我们来看下面我使用了三次 ,而第三次其实并没有生效
返回目录
当我们调用Vueponent()注册组件时,组件的注册是全局的
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app2"><cpn></cpn>
</div><div id="app"><cpn></cpn><cpn></cpn><cpn></cpn>
</div><script src="../js/vue.js"></script>
<script>// 1.创建组件构造器const cpnC = d({template:`<div><h2>我是标题</h2><p>我是内容,哈哈哈哈</p><p>我是内容,呵呵呵呵</p></div>`});// 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用)/*Vueponent('cpn',cpnC)*/const app = new Vue({el: '#app',data: {message: '你好啊'},components:{// cpn使用组件时的标签名,局部注册!!!cpn: cpnC}});const app2=new Vue({el:'#app2'})
</script>
</body>
</html>
通过Vueponent()方法注册的组件是全局组件,通过 components 注册的是私有子组件
返回目录
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body><div id="app"><cpn2></cpn2></div><script src="../js/vue.js"></script><script>// 1.创建第一个组件构造器(子组件)<只能在父组件中使用,想要外部使用,在外部也要进行注册>const cpnC1d({template:`<div><h2>我是标题</h2><p>我是内容,哈哈哈</p></div>`})// 2.创建第一个组件构造器(父组件)const cpnC2d({template:`<div><h2>我是标题2</h2><p>我是内容,呵呵呵</p><cpn1></cpn1></div>`,components:{cpn1:cpnC1}})// root组件const app = new Vue({el: '#app',data: {message: '你好啊'},components: {cpn2: cpnC2}});</script>
</body>
</html>
父子组件的错误用法: 以子组件的形式在 Vue 实例中使用
<div id="app"><cpn2></cpn2><!--父子组件的错误用法:以子组件的形式在 Vue 实例中使用--> <!--<cpn1></cpn1>-->
</div><script src="../js/vue.js"></script>
<script>// 1.创建第一个组件构造器(子组件)const cpnC1 = d({template: `<div><h2>我是标题1</h2><p>我是内容, 哈哈哈哈</p></div>`})// 2.创建第二个组件构造器(父组件)const cpnC2 = d({template: `<div><h2>我是标题2</h2><p>我是内容, 呵呵呵呵</p><cpn1></cpn1></div>`,components: {// 在父组件中注册子组件,这样就可以在父组件里面使用子组件// 例如上面的<cpn1></cpn1> cpn1: cpnC1}})// root组件const app = new Vue({el: '#app',data: {message: '你好啊'},components: {// cpn1子组件在cpn2父组件中注册,父组件cpn2在Vue实例里面注册cpn2: cpnC2}})
</script>
返回目录
Vue 为了简化这个过程,提供了注册的语法糖
主要是省去了调用 d() 的步骤,而是可以直接使用一个对象来代替
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn></cpn><cpn2></cpn2>
</div><script src="../js/vue.js"></script>
<script>// 1.全局组件注册的语法糖// 不推荐的写法:const cpn = d()//2.注册组件(全局组件)Vueponent('cpn',{template:`<div><h2>我是标题</h2><p>我是内容,哈哈哈</p></div>`})const app = new Vue({el: '#app',data: {message: '你好啊'},components:{'cpn2':{template: `<div><h2>我是标题2</h2><p>我是内容,哈哈哈</p></div>`}}});
</script>
</body>
</html>
返回目录
通过语法糖简化了 Vue 组件的注册过程,另外还有一个地方的写法比较麻烦,就是 template 模块写法
使用 script 标签:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn></cpn><cpn></cpn><cpn></cpn>
</div><!--1.script标签, 注意:类型必须是text/x-template-->
<script type="text/x-template" id="cpn"><div><h2>我是标题</h2><p>我是内容,哈哈哈</p></div>
</script><script src="../js/vue.js"></script>
<script>// 1.注册一个全局组件Vueponent('cpn', {template: '#cpn'})const app = new Vue({el: '#app',data: {message: '你好啊'}})
</script></body>
</html>
重点:使用template标签🔥
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body><div id="app"><cpn></cpn><cpn></cpn><cpn></cpn>
</div><!--2.template标签-->
<template id="cpn"><div><h2>我是标题</h2><p>我是内容,呵呵呵</p></div>
</template><script src="../js/vue.js"></script>
<script>// 1.注册一个全局组件Vueponent('cpn', {template: '#cpn'})const app = new Vue({el: '#app',data: {message: '你好啊'}})
</script></body>
</html>
返回目录
组件可以访问Vue实例数据吗?
结论:组件不能直接访问Vue实例中的 data
注:即使访问放也不建议写在Vue实例中
组件是一个单独功能模块的封装:
组件自己的数据存放在哪呢?
<div id="app"><cpn></cpn><cpn></cpn><cpn></cpn>
</div><!--2.template标签-->
<template id="cpn"><div><h2>{{title}}</h2><p>我是内容,呵呵呵</p></div>
</template><script src="../js/vue.js"></script>
<script>// 1.注册一个全局组件Vueponent('cpn', {template: '#cpn',data() {return {title: 'abc'}}})const app = new Vue({el: '#app',data: {message: '你好啊',// title: '我是标题'}})
</script>
为什么 data 在组件中必须是一个函数呢?
返回目录
在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的
但是,在开发中,往往一些数据确实需要从上层传递到下层
如何进行父子组件间的通信呢?
在组件中,使用选项 props 来声明需要从父级接收到的数据(properties)
props 的值有两种方式:
传数组:
<div id="app"><!-- 4.使用组件(v-bind动态绑定) --> <cpn :cmessage="message" :cmovies="movies"></cpn>
</div><!-- 2.组件构造器的模板分离写法 -->
<template id="cpn"><div><h1>{{cmovies}}}</h1><h1>{{cmessage}}</h1></div>
</template><script src="../js/vue.js"></script>
<script>// 1.创建组件构造器(子组件)const cpn = {template: '#cpn',props: ['cmovies', 'cmessage'] //父传子,props}// 3.注册组件(将子组件在父组件里面注册)const app = new Vue({el: '#app',data: {message: '你好啊',movies: ['海王', '海贼王', '海尔兄弟']},components: {//对象字面量增强写法的属性增强写法cpn}})
</script>
注意:我们在使用组件时,需要用v-bind 动态绑定数据。
传对象:
验证支持的数据类型有:
①类型限制
我们可以在 props 里面限制父组件给子组件传递的数据类型
<!--父组件模板-->
<div id="app"><cpn :cmessage="message" :cmovies="movies"></cpn>
</div><!--子组件模板-->
<template id="cpn"><div><h1>{{cmovies}}}</h1><h1>{{cmessage}}</h1></div>
</template><script src="../js/vue.js"></script>
<script>// 父传子: propsconst cpn = {template: '#cpn',props: {// 1.类型限制cmovies: Array, // 限制父组件传的是数组类型cmessage: String, // 限制父组件传的是字符串类型}}// root组件,我们当作父组件const app = new Vue({el: '#app',data: {message: '你好啊',movies: ['海王', '海贼王', '海尔兄弟']},components: {//对象字面量增强写法的属性增强写法cpn}})
</script>
②默认值和必传值
<div id="app"><!--在这里传值--> <cpn :cmessage="message" :cmovies="movies"></cpn>
</div><template id="cpn"><div><h1>{{cmovies}}}</h1><h1>{{cmessage}}</h1></div>
</template><script src="../js/vue.js"></script>
<script>// 父传子: propsconst cpn = {template: '#cpn',// props: ['cmovies', 'cmessage'],props: {// 2.提供一些默认值, 以及必传值cmessage: {type: String, // 类型限制为 Stringdefault: 'aaaaaaaa', // 如果没有传值,则给一个默认值required: true // required 必须的,即意味着这个值是必须要传递的,不传就报错},// 类型是对象或者数组时, 默认值必须是一个函数cmovies: {type: Array,default() {return []}}},// root组件,我们当作父组件const app = new Vue({el: '#app',data: {message: '你好啊',movies: ['海王', '海贼王', '海尔兄弟']},components: {//对象字面量增强写法的属性增强写法cpn}})
</script>
③自定义类型
当我们有自定义构造函数时,验证也支持自定义的类型
④props驼峰标识
当我们 props 里面的属性是驼峰写法的时,在传入值时需要进行 - 连接
<div id="app"><!--目前不支持直接写cInfo,驼峰得加 `-` 连接--><cpn :c-info="info" :child-my-message="message" ></cpn>
</div><template id="cpn"><div><h2>{{cInfo}}</h2><h2>{{childMyMessage}}</h2></div>
</template><script src="../js/vue.js"></script>
<script>const cpn = {template: '#cpn',props: {// 驼峰写法cInfocInfo: {//类型是对象或者数组时, 默认值必须是一个函数type: Object,default() {return {}}},childMyMessage: {type: String,default: ''}}}const app = new Vue({el: '#app',data: {info: {name: 'why',age: 18,height: 1.88},message: 'aaaaaa'},components: {cpn}})
</script>
返回目录
什么时候需要自定义事件呢?
自定义事件的流程:
我们来看一个简单的例子:
我们之前做过一个两个按钮 +1 和 -1,点击后修改 counter
我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件
这样,我们就需要将子组件中的 counter,传给父组件的某个属性,比如total
<body><!--父组件模板-->
<div id="app"><!-- 2.父组件里面接收子组件发出的自定义事件 值是一个方法 --><cpn @item-click="cpnClick"></cpn>
</div><!--子组件模板-->
<template id="cpn"><div><button v-for="item in categories"@click="btnClick(item)">{{item.name}}</button></div>
</template><script src="../js/vue.js"></script>
<script>// 1.子组件const cpn = {template: '#cpn',data() {return {categories: [{id: 'aaa', name: '热门推荐'},{id: 'bbb', name: '手机数码'},{id: 'ccc', name: '家用家电'},{id: 'ddd', name: '电脑办公'},]}},methods: {btnClick(item) {// console.log(item);// 1.发射事件: 自定义事件// 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数this.$emit('item-click', item)//发射}}}// 2.父组件const app = new Vue({el: '#app',data: {message: '你好啊'},components: {cpn},methods: {// 3.父组件里面定义方法处理cpnClick(item) {console.log('cpnClick', item);}}})
</script></body>
返回目录
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body><div id="app"><cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<template id="cpn"><div><h2>props:{{number1}}</h2><h2>data:{{dnumber1}}</h2><!--<input type="text" v-model="dnumber1">--><!--不建议绑定props里面的值--><input type="text" :value="dnumber1" @input="num1Input"><h2>props:{{number2}}</h2><h2>data:{{dnumber2}}</h2><!--<input type="text" v-model="dnumber2">--><input type="text" :value="dnumber2" @input="num2Input"></div>
</template>
<script src="../js/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {num1:1,num2:0},methods:{num1change(value){this.num1=Number(value);},num2change(value){this.num2=Number(value);}},components:{cpn:{template:'#cpn',props:{number1:Number,number2:Number,},data(){return{dnumber1:this.number1,dnumber2:this.number2}},methods:{num1Input(event){this.dnumber1=event.target.value;this.$emit('num1change',this.dnumber1)},num2Input(event){this.dnumber2=event.target.value;this.$emit('num2change',this.dnumber2)}}}}});
</script></body>
</html>
返回目录
返回目录
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<template id="cpn"><div><h2>props:{{dnumber1}}</h2><h2>data:{{dnumber1}}</h2><input type="text" v-model="dnumber1"><h2>props:{{dnumber2}}</h2><h2>data:{{dnumber2}}</h2><input type="text" v-model="dnumber2"></div>
</template><script src="../js/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {num1:1,num2:0},methods:{num1change(value){this.num1=value;},num2change(value){this.num2=value;}},components:{cpn:{template:'#cpn',props:{number1:Number,number2:Number,},data(){return{dnumber1:this.number1,dnumber2:this.number2}},watch:{dnumber1(newValue){this.dnumber1=newValuethis.$emit('num1change',wValue)},dnumber2(newValue){this.dnumber2=newValuethis.$emit('num2change',wValue)}}}}});
</script>
</body>
</html>
返回目录
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。
父组件访问子组件:使用 $children 或 $refs
子组件访问父组件:使用$parent
我们先来看下$children (它是一个复数)的访问
$children 的缺陷:
$refs 的使用🔥
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<!--父组件的模板-->
<div id="app"><cpn></cpn><cpn></cpn><cpn ref="aaa"></cpn><button @click="btnClick">按钮</button>
</div><!--子组件的模板-->
<template id="cpn"><div>我是子组件</div>
</template><script src="../js/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {message: '你好啊'},methods: {btnClick() {// 2.$refs => 对象类型, 默认是一个空的对象 必须在组件上加 ref='bbb'/*console.log(this.$children)//$children不常使用因为不建议使用下标值取值this.$children[0].showMessage();this.$children[0].name;*/console.log(this.$refs.aaa.name);}},components: {cpn: {template: '#cpn',data() {return {name: '我是子组件的name'}},methods: {showMessage(){console.log('我是子组件的方法')}}},}})
</script>
</body>
</html>
返回目录
如果我们想在子组件中直接访问父组件,可以通过 $parent
尽管在 Vue 开发中,我们允许通过 $parent 来访问父组件,但是在真实开发中尽量不要这要做
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了
如果我们将子组件放入另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题
$parent(了解即可)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<!--父组件的模板-->
<div id="app"><cpn></cpn>
</div><!--子组件的模板-->
<template id="cpn"><div><h2>我是cpn组件</h2><ccpn></ccpn></div>
</template><template id="ccpn"><div><h2>我是ccpn子组件</h2><button @click="btnClick">按钮</button></div>
</template><script src="../js/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {message: '你好啊'},components: {cpn: {template: '#cpn',data(){return{name:'我是cpn组件的name'}},components: {ccpn: {template: '#ccpn',methods: {btnClick() {// 1.访问父组件$parentconsole.log(this.$parent)// 2.访问父组件的nameconsole.log(this.$parent.name);}}}}}}})
</script>
</body>
</html>
$root(也用的比较少)
返回目录
slot翻译为插槽
组件的插槽
栗子:移动网站中的导航栏
如何封装这类组件呢?
如何封装合适呢?抽取共性,保留不同
说明图:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn><button>我是按钮</button></cpn><cpn><span>我是span</span></cpn><cpn><cpn></cpn></cpn><cpn><button>按钮1</button><button>按钮2</button><button>按钮3</button></cpn>
</div><template id="cpn"><div><h2>我是组件</h2><p>我是组件,哈哈哈</p><slot><button>外界未指定则显示此默认</button></slot></div>
</template><script src="../js/vue.js"></script><script>const app = new Vue({el: '#app',data: {message: '你好啊'},components:{cpn: {template:'#cpn'}}});
</script></body>
</html>
运行结果:
返回目录
当子组件的功能复杂时,子组件的插槽可能并非是一个。
如何给插槽起名字呢?
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn><span slot="center">中间标题</span><button slot="right">右边按钮</button></cpn>
</div><template id="cpn"><div><slot name="left"><span>左边</span></slot><slot name="center"><span>中间</span></slot><slot name="right"><span>右边</span></slot><!--<slot>哈哈哈</slot>外界没有指明名字则只会替换无名插槽--></div>
</template><script src="../js/vue.js"></script><script>const app = new Vue({el: '#app',data: {message: '你好啊'},components:{cpn: {template:'#cpn'}}});
</script></body>
</html>
返回目录
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn v-show="isShow"></cpn><!--父组件的作用域优先父组件属性-->
</div><template id="cpn"><div><h2>我是子组件</h2><p>我是内容哈哈哈</p><button v-show="isShow">按钮</button><!--子组件的作用域优先子组件属性--></div>
</template><script src="../js/vue.js"></script><script>const app = new Vue({el: '#app',data: {message: '你好啊',isShow: true},components:{cpn: {template:'#cpn',data(){return{isShow:false}}}}});
</script></body>
</html>
答案:最终可以渲染出来,也就是使用的是 Vue 实例的属性
为什么呢?
返回目录
一句话总结:父组件替换插槽的标签,但是内容由子组件来提供
我们先提一个需求:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Title</title>
</head>
<body>
<div id="app"><cpn></cpn><cpn><!--目的是获取子组件的pLanguages--><!--VUE2.5版本以下必须使用template标签,之后版本可以使用任意标签--><template v-slot="slot"><!--slot-scope="slot" VUE2.6版本之前使用,2.6之后用的是v-slot--><span v-for="item in slot.data">{{item}}-</span><!--难点在于理解slot.data--></template></cpn><cpn><template slot-scope="slot"><span v-for="item in slot.data">{{item}}***</span></template></cpn><cpn><template slot-scope="slot"><span>{{slot.data.join('***')}}</span><!--去掉最后面的****--></template></cpn>
</div><template id="cpn"><div><slot :data="pLanguages"><ul><li v-for="item in pLanguages">{{item}}</li></ul></slot></div>
</template><script src="../js/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {message: '你好啊'},components:{cpn:{template:'#cpn',data(){return{pLanguages:['javascript','python','Swift','Go','C++','java','C#']}}}}});
</script>
</body>
</html>
返回目录
JavaScript原始功能
在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。
随着ajax异步请求的出现,慢慢形成了前后端的分离
比如全局变量同名问题
另外,这种代码的编写方式对js文件的依赖顺序几乎是强制性的
匿名函数的解决方案
返回目录
ES6export指令
// info.js
export let name = 'why'
export let age = 18
export let height = 1.88
// info.js
let name = 'why'
let age = 18
let height = 1.88export{name,age,height}
详情点击
返回目录
导出函数或类
上面我们主要是输出变量,也可以输出函数或者输出类
export function test(content){console.log(content);
}
export class Person {constructor(name,age){this.name = name;this.age = age}run(){console.log(this.name + '在奔跑');}
}
上面的代码也可以写成这种形式
function test(content) {console.log(content);
}class Person {constructor(name,age) {this.name = name;this.age = age;}run() {console.log(this.name + '在奔跑')}
}export {test,Person}
export default
// info.js
export default function() {console.log('default function')
}
我们来到 main.js 中,这样使用就可以了
import myFunc form './info.js'myFunc()
另外,需要注意:
ES6 import的使用
我们使用 export 指令导出了模块对外提供的接口,下面我们就可以通过 import 命令来加载对应的这个模块了
<script src = "info.js" type = "module"></script>
<script src = "main.js" type = "module"></script>
import 指令用于导入模块中的内容,比如 main.js 的代码
import {name,age,height} from "./info.js"console.log(name,age,height);
import * as info from './info.js'console.log(info.name,info.age,info.height);
返回目录
内容概述
认识webpack
打包
和grunt/gulp的对比
grunt / gulp 的核心是 Task
我们来看一个 gulp 的task
什么时候用 grunt / gulp 呢?
安装webpack
安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm
安装node.js
点击next
是否勾选工具去编译 native 模块,我们不勾选
然后等待安装完成!
测试
使用管理员方式打开dos窗口,输入 node -v
显示如图样式,则安装成功
更改淘宝镜像
管理员方式打开DOS窗口,输入
npm config set registry
点击回车就更换成功了,可以通过
npm config get registry
上面命令去查看是否成功,如果返回是如下图则更换成功
全局安装webpack
webpack --version
如果你看到3.6.0,说明你已经成功安装了webpack3.6.0
返回目录
首先创建如下文件和文件夹
dist文件夹: 用于存放之后打包的文件
src 文件夹: 用于存放我们写的源文件
index.html 浏览器打开展示的首页html
package.json 通过 npm init 生成的,npm 包管理的文件
我们应该使用webpack工具,对多个 js 文件进行打包(注意路径)
webpack 12-认识webpack/src/main.js 12-认识webpack/dist/bundle.js
使用打包后的文件
打包后会在 dist 文件夹下,生成一个 bundle.js 文件
<script src="dist/bundle.js"></script>
示例
例如,我们使用模块化开发两个js文件
之后在 main.js 中进行引入两个js文件
在Termial终端使用 webpack 打包main.js
webpack 12-认识webpack/src/main.js 12-认识webpack/dist/bundle.js
运行测试:
所以我们就知道了,我们以后在 src 下开发,之后让 webpack 打包main.js,然后我们引用打包后的js文件,这就是我们的开发模式。
返回目录
每次使用webpack的命令都需要写上入口和出口作为参数,非常麻烦,有没有一种方法可以将者两个参数写到配置中,在运行时,直接读取呢?
之前我们都是安装的全局webpack,现在安装本地的
既然已经全局安装了 webpack,为什么还要局部安装呢?这是因为我们需要的项目可能是从网上下载下来的,项目中使用的webpack和我们本地的webpack可能版本不同,这就需要在局部安装webpack了
目前,我们使用的webpack是全局的webpack,如果我们想使用局部来打包呢?
因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题。
所以通常一个项目,都有自己局部的webpack。
安装本地webpack:
dev为开发时依赖
后面我们会使用运行时依赖
当我们在终端和Terminal中进行打包命令,用的都是全局webpack
当使用我们配置的命令例如npm run build,优先使用本地webpack打包
再次声明webpack的打包是为了模块化开发,将我们的模块化导入导出等等命令翻译为浏览器可以识别的代码
返回目录
loader是webpack中一个非常核心的概念。
webpack用来做什么呢?
在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。
但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。
对于webpack本身的能力来说,对于这些转化是不支持的。
那怎么办呢?给webpack扩展对应的 loader 就可以啦。
loader使用过程:
步骤一:通过 npm 安装需要使用的 loader
步骤二:在 fig.js 中的 modules 关键字下进行配置
在实际开发过程中, webpack 默认只能打包处理以 .js 后缀名结尾的模块。其他 非 .js 后缀名结尾的模块 , webpack 默认处理不了, 需要调用 loader 加载器才可以正常打包 ,否则会报错
loader加载器的作用: 协助 webpack 打包处理特定的文件模块 。比如:
首先介绍css文件处理准备工作
建立我们的css文件步骤
在src目录中,创建一个css文件,其中创建一个normal.css文件。
我们也可以重新组织文件的目录结构,将零散的js文件放在一个js文件夹中。
normal.css 中的代码非常简单,就是将body设置为red
在main.js中导入css的模块依赖
此时我们进行打包是不会成功的
现在开始安装我们的loader
在webpack中文网中有 loader的用法:/
css 文件的打包需要用到 style-loader,css-loader
先安装 css-loader
在Terminal中输入以下命令安装css-loader:
npm install --save-dev css-loader
引入依赖我们在main.js中已经做了
//3.依赖css文件
require('./css/normal.css')
在fig.js中导入配置:
module: {rules: [{test: /.css$/,use: [ 'style-loader', 'css-loader' ]}]}
style-loader也需要安装,因为css-loader只负责将css文件加载不会渲染页面,页面渲染还得靠style-loader
在Terminal中输入以下命令安装style-loader:
npm install style-loader --save-dev
其余配置不变
注意点:
npm install --save-dev style-loader@0.23.1
测试结果:
运行结果:
返回目录
如果我们希望在项目中使用less、scss、stylus来写样式,webpack是否可以帮助我们处理呢?
我们这里以less为例,其他也是一样的。
我们还是先创建一个less文件,依然放在css文件夹中
第一步:仍然是安装:
npm install --save-dev less-loader less
第二步:在main.js中导入.less文件依赖:
第三步:配置fig.js:
{test: /.less$/,use: [{loader: "style-loader" // creates style nodes from JS strings}, {loader: "css-loader" // translates CSS into CommonJS}, {loader: "less-loader" // compiles Less to CSS}]}
打包测试:
老规矩,报错无法识别函数的改版本:
npm install less@3.9.0 less-loader@4.1.0 --save-dev
运行测试:perfect!!!
当然我们也可以在main.js文件中写我们的文字,使用document.write()或document.writeln()
重新打包运行吧,效果一致
返回目录
首先,我们在项目中加入两张图片:
在src包下建立img包并放入我们的两张图片
url-loader
图片处理
npm install --save-dev url-loader
{test: /.(png|jpg|gif)$/,use: [{loader: 'url-loader',options: {limit: 8192}}]}
打包打包!
注意你的图片是jpeg格式还有添加约束
打包成功!
运行测试:第一次失败显示
在options中添加esModule:false(移除 ES Modules 下的严格模式)
再次打包运行测试:成功
下载file-loader吧:
npm install file-loader --save-dev
当加载图片时,小于limit时,会将图片编译成base64字符串形式
当加载的图片,大于limit时,需要使用file-loader模块进行加载
再次打包运行测试:
改版本:
npm install file-loader@4.0.0 --save-dev
打包
而且还自动帮我们生成了新名字的图片
在dist文件夹中的图片我们是没有写入路径的因此显示失败
此时我们又需要改配置了(对于大于限制的图片才会用上此配置)
运行测试:成功!
我们发现webpack自动帮助我们生成一个非常长的名字,这是一个32位hash值,目的是防止名字重复,但是,真实开发中,我们可能对打包的图片名字有一定的要求,比如,将所有的图片放在一个文件夹中,跟上图片原来的名称,同时也要防止重复(img/name.)。
所以,我们可以在options中添加上如下选项:
例子:
打包!!!得到我们想要的规范
运行也没问题:
返回目录
webpack只能打包处理一部分 高级的 JavaScript 语法。对于那些 webpack 无法处理的高级 js 语法,需要借助于 babelbabel-loader 进行打包处理。
如果希望将ES6的语法转成ES5,那么就需要使用 babel-loader 进行打包处理(照顾浏览器的兼容性)
安装 babel-loader
npm install -D babel-loader @babel/core @babel/preset-env webpack
配置 fig.js 文件
{test: /.m?js$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: [['@babel/preset-env', { targets: "defaults" }]]}}}
再次打包寻找ES6语法:(你找let也没有)
返回目录
之前我们配置打包的都是普通的js代码
现在开始我们的Vue配置了
后续的项目中我们都是使用Vue.js来进行开发,而且会以特殊的文件来组织vue的组件
现在,我们希望在项目与中使用Vuejs,那么必然要对其有依赖,所以需要先进行安装
注:因为我们后续是在实际项目中也会使用vue的,使用并不是开发时依赖
npm install vue --save
那么,接下来就可以按照我们之前学习的方式来使用Vue了
回顾一下安装Vue的三种方式:
要保证node_modules包里有Vue
注:如果没有下载本地webpack的输入下面命令
npm install webpack@3.6.0 --save-dev
下载vue,注意不能在加–dev(开发时依赖),因为vue是我们开发运行时都在使用的
npm install vue --save
下载成功
在main.js中引用Vue:
在index.html中使用:
打包成功并测试:(出现报错)
说明几个问题:
vue在构建最终发布版本的时候构建了两类版本
错误信息告诉我们正在使用runtime-only版本
解决方案:指定版本
在fig.js中进行配置:
resolve:{//alias:别名alias:{'vue$':'vue/dist/vue.esm.js'}}
指定导入的vue是哪个文件
注意点:不要把js文件放在head标签里面!!!
运行测试:成功
返回目录
真实开发中是不需要写const app的直接new Vue使用即可
在页面开发中只会有一个index.html(了解)
单页面复用技术:SPA(simple page web application)–>多个页面通过路由跳转:vue-router(前端路由)
而且在index页面中只会存在:<div id=“app”></div>
返回目录
在main.js中这样写:
//5.使用vue进行开发
import Vue from 'vue'const App={template:`<div><h2>{{message}}</h2><button @click="btnClick">按钮</button><h2>{{name}}</h2></div>`,data(){return{message:'hello webpack',name:'abc'}},methods:{btnClick(){console.log(1111)}}
}new Vue({el:'#app',template: '<App/>',components:{App}
})
这样写也不是最优,接下来我们继续优化:
在src包下建立Vue
建立app.js文件
将刚刚的对象写入app.js中,作为导出
然后在main.js中导入
打包测试:成功!
在src包下创建.vue文件
在vue文件中,模板布局都给我们定义好了
移入我们之前的代码:
<template><div><h2 class="title">{{message}}</h2><button @click="btnClick">按钮</button><h2>{{name}}</h2></div>
</template><script>export default {name: "App",data(){return{message:'hello webpack',name:'abc'}},methods:{btnClick(){console.log(1111)}}}
</script><style scoped>.title{color: green;}
</style>
删掉app.js,在main.js中重新导入:
因为.vue是特殊文件,所以也需要配置对应的loader
需要两个东西:
第一步安装:
npm install --save-dev vue-loader vue-template-compiler
在fig.js中进行配置:
打包报错:
在package.json中修改版本
重新安装
在Terminal中重新打包:
运行测试:成功
第一步:在vue文件夹下新建立Cpn.vue
第二步:写上我们的代码
第三步:在App.vue中可以注册并使用(其他地方也可以注册使用)
重新打包运行测试:成功!!!
以后开发是以组件树的模式,App.vue就可能是根组件,而且每个组件都是一个独立的vue文件
当我们使用脚手架以后,配置内容再也不需要我们手动写了!!!
返回目录
plugin是什么?
loader 和 plugin 区别?
plugin 使用步骤:
步骤一:通过npm安装需要使用的plugins(某些webpack已经内置的插件不需要安装)
步骤二:在fig.js中的plugins中配置插件
下面,我们就来看看可以通过哪些插件对现有的webpack打包过程进行扩容,让我们的webpack变得更加好用
在webpack中文网中有plugin的用法:/
步骤如下:
在 fig.js 中导入 webpack
const webpack = require('webpack');
配置在 plugins 中
ports = {...plugins: [new webpack.BannerPlugin('最终版权归ldx所有')]
}
打包:
返回目录
目前,我们的index.html文件是存放在项目的根目录下的。我们知道,在真实发布项目时,发布的是dist文件夹中的内容,但是dist文件夹中如果没有index.html文件,那么打包的js等文件也就没有意义了。所以,我们需要将index.html文件打包到dist文件夹中,这个时候就可以使用html-webpack-plugin插件
html-webpack-plugin 插件可以为我们做这些事情:
安装
npm install html-webpack-plugin@3.2.0 --save-dev
在 fig.js 中导入 HTML 插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
打包:自动生成index.html代码
优化:
现在的问题就是要让<div id=“app”></div>自动生成
且原index.html文件中的<script type=“text/javascript” src=“bundle.js”></script>可以去掉
在fig.js中配置:
打包:
返回目录
在项目发布之前,我们必然需要对js等文件进行压缩处理
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
修改fig.js文件,使用插件:
查看打包后的bunlde.js文件,是已经被压缩过了
返回目录
webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。
类似于 node.js 阶段用到的 nodemon 工具,每当修改了源代码, webpack 会自动进行项目的动态打包构建(没有真实的映射到硬盘,所以最后测试完发布的时候只需要必须执行一次打包命令)
npm install webpack-dev-server@2.9.1 --save-dev
配置:
配置命令:
输入npm run dev指令
测试:
服务器运行时我们进行动态修改代码就行了,最后部署的时候一定要执行打包命令
优化配置,不需要我们手动访问浏览器
测试指令npm run dev,完成(Ctrl+C中断服务器)
返回目录
场景引入:
这时候就需要我们进行单独分离了,把测试时和打包发布时的配置分离出来
平时小项目且生产环境和开发环境不是很复杂的时候是不需要分离的,而且Vue脚手架也是做了分离的
建立build文件夹和三个js文件
现在的问题是怎么在不同的阶段组合配置文件
下载安装webpack-merge(merge是合并的意思)
npm install webpack-merge
/*动态获取路径第一步导入我们的path*/
const path =require('path')/*这个path文件存在node包中,需要我们手动装*/const webpack = require('webpack');const HtmlWebpackPlugin=require('html-webpack-plugin');ports={entry:'./src/main.js',output:{solve(__dirname,"dist"),/*动态获取我们的路径,resolve方法拼接,__dirname为所依赖的路径*/filename:'bundle.js',/*publicPath:'dist/'不再需要*/},module: {rules: [{test: /.css$/,use: [ 'style-loader' , 'css-loader']},{test: /.less$/,use: [{loader: "style-loader" // creates style nodes from JS strings}, {loader: "css-loader" // translates CSS into CommonJS}, {loader: "less-loader" // compiles Less to CSS}]},{test: /.(png|jpg|gif|jpeg)$/,use: [{loader: 'url-loader',options: {limit: 8192,esModule:false,name: 'img/[name].[hash:8].[ext]'},}]},{test: /.m?js$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: [['@babel/preset-env', { targets: "defaults" }]]}}},{test: /.vue/,use: ['vue-loader']}]},resolve:{//alias:别名alias:{'vue$':'vue/dist/vue.esm.js'}},plugins:[new webpack.BannerPlugin('最终版权归ldx所有'),new HtmlWebpackPlugin({template:'index.html'}),]
}
const webpackMerge=require('webpack-merge')
const baseConfig=require('./fig')ports= (baseConfig,{//高版本的webpack-merge是一个对象,需要调用其merge方法才能正常运行devServer:{contentBase:'./dist',/*为哪一个文件夹提供本地服务,默认是根文件夹,我们这里填写./dist*/inline:true/*页面实时刷新*/}
})
const UglifyJsWebpackPlugin=require('uglifyjs-webpack-plugin')
const webpackMerge=require('webpack-merge')
const baseConfig=require('./fig.js')ports(baseConfig,{//高版本的webpack-merge是一个对象,需要调用其merge方法才能正常运行plugins:[new UglifyJsWebpackPlugin()]
})
删除原来的fig.js文件
在package.json中配置命令:
打包:路径问题
打包位置在build下的dist包中,需要我们修改fig.js文件的输出路径:
再次打包:
路径正确:
可以试试npm run dev:
也成功运行:
动态添加内容试试:
浏览器也是实时刷新:
本文发布于:2024-01-29 19:23:56,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170652744017716.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |