文章目录
- 1. 父级向子级组件传值
- 2. 子级向父级组件传值
- 3. 平行组件传值
- 4. Vue全家桶是什么
- 5. 为什么要使用单页面应用
- 6. 安装vue-cli脚手架
- 7. 过滤器的使用
- 8. 生命周期的钩子函数
- 9. router的基本使用
- 10. 命名路由和动态路由匹配
- 11. 编程式导航
- 12. refs属性的使用
- 13. 模块化初探索
- 14. Vue的模块化初探索
- 15. webpack的使用
- 16. webpack中loader的使用
- 17. webpack中plugin的使用
- 18. 单文件的使用
- 19. 单页面SPA应用
- 20. css中scoped的使用
- 21. vue-cli的使用
- 22. element-ui的使用
Github: https://github.com/ThanlonSmith/vue-tutorial-chinese
父组件中可以使用静态绑定传入子组件的值,也可以动态绑定传入子组件的值:
<html lang="zh-CH">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父级向子级组件传值title>
head>
<body>
<div id="app">
<App>App>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
Vue.component('VBtn', {
data() {
return {
}
},
template: `
`,
props: ['id'],
methods: {
clickHandler() {
// 每个组件中的this指的是当前组件对象
console.log(this) // VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
},
created() {
console.log(this) // VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
})
let Vheader = {
data() {
return {
text: '我是子组件中的数据!'
}
},
template: `
{
{ text }}分割线下面是来自父组件传过来的值:
msg:{
{ msg }}
post.id:{
{ post.id }}
post.title:{
{ post.title }}
`,
// 挂载父组件属性。只要声明了父组件的属性就可以使用
props: ['msg', 'post'],
methods: {
// 由于是VBtn调用clickHandler,所以这里的clickHandler不会被执行
clickHandler() {
alert(1)
}
},
created() {
console.log(this) // VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
}
let App = {
data() {
return {
text: '我是父组件中准备传入子组件中的数据,可以使用v-bind动态绑定也可以静态绑定!',
post: {
id: 1,
title: '我是父组件自定的属性title!'
}
}
},
template: `
`,
components: {
Vheader: Vheader
},
created() {
console.log(this) // VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
}
let vm = new Vue({
el: '#app',
data() {
return {
}
},
components: {
App
},
created() {
console.log(this) // Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
}
})
script>
html>
这里沿用第1部分做的:
<html lang="zh-CH">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>子组件向父组件传值title>
head>
<body>
<div id="app">
<App>App>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
Vue.component('VBtn', {
data() {
return {
}
},
template: `
`,
props: ['id'],
methods: {
clickHandler() {
// 每个组件中的this指的是当前组件对象
console.log(this) // VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
// this.$emit('父组件声明自定义的事件', '传值')
console.log('VBtn组件:', this.id)
this.$emit('VheaderHandler', this.id)
}
},
created() {
console.log(this) // VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
},
})
let Vheader = {
data() {
return {
text: '我是子组件中的数据!'
}
},
template: `
{
{ text }}分割线下面是来自父组件传过来的值:
msg:{
{ msg }}
post.id:{
{ post.id }}
post.title:{
{ post.title }}
`,
// 挂载父组件属性。只要声明了父组件的属性就可以使用
props: ['msg', 'post'],
methods: {
// 由于是VBtn调用clickHandler,所以这里的clickHandler不会被执行
VheaderHandler(val) {
// @VheaderHandler与其值可以不一样,但必须与子组件中的this.$emit('VheaderHandler', this.id)中的VheaderHandler一样
console.log('Vheader组件:' + val)
this.$emit('AppHandler', val)
}
},
created() {
console.log(this) // VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
}
let App = {
data() {
return {
text: '我是父组件中准备传入子组件中的数据,我需要通过v-bind绑定才能使用!',
post: {
id: 1,
title: '我是父组件自定的属性title!'
}
}
},
template: `
`,
components: {
Vheader: Vheader
},
created() {
console.log(this) // VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
},
methods: {
app_handler(val) {
console.log('App组件:' + val)
this.post.id = val
}
}
}
let vm = new Vue({
el: '#app',
data() {
return {
}
},
components: {
App
},
created() {
console.log(this) // Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
}
})
script>
html>
<html lang="zn-CH">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>平行组件传值title>
head>
<body>
<div id="app">
<App>App>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
// Test1组件向Test2传值,Test2要声明事件:$on('事件的名字',function(){}),Test1要触发事件:$emit('Test声明的事件的名字','值')
// 前提:这两个方法必须同一个实例化对象“bus”上
let bus = new Vue() // 新建Vue对象作为两个方法共同绑定的实例化对象
Vue.component('Test2', {
data() {
return {
txt: '我是Test2组件!',
text: ''
}
},
template: `
{
{ txt }}
{
{ text }}
`,
created() {
bus.$on('testData', (val) => {
console.log(bus)
console.log('传递过来的值:' + val)
this.text = val
})
}
})
Vue.component('Test1', {
data() {
return {
msg: '收到来自Test1组件的数据!'
}
},
props: ['txt'],
template: `
`,
methods: {
clickHandler() {
console.log("bus:" + bus)
bus.$emit('testData', this.msg)
}
}
})
let Vheader = {
data() {
return {
txt: '传递'
}
},
template: `
`
}
let App = {
data() {
return {
}
},
template: `
`,
components: {
Vheader: Vheader
}
}
let vm = new Vue({
el: '#app',
data() {
return {
}
},
components: {
App: App
}
})
script>
html>
vue和vue-router以及vuex三者合起来是Vue的全家桶,Vue全家桶主要用来做SPA(Simple Page Application)单页面应用。
传统的路由跳转,如果后端资源比较多,会导致页面出现 “白屏” 现象。让前端来做路由,在某个生命周期函数中发送 AJAX,数据驱动视图,提升用户体验。
淘宝npm镜像:https://developer.aliyun.com/mirror/NPM?from=tnpm
vue-cli官网:https://cli.vuejs.org/zh/guide/
# 安装cnpm
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
# 安装vue-cli
$ npm install -g @vue/cli
局部过滤器:在当前组件内部使用过滤器。使用过滤器其实就是给某系数据“添油加醋”
,使用的格式(伪代码):
//声明
filters:{
'过滤器的名字':function(val,a,b){
// val就是当前的数据,a和b是参数
}
}
//使用管道符
数据 | 过滤器的名字('erics','thanlon') // a='erics',b='thanlon',管道符|左右的空格也可以删除
局部过滤器使用示例:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>过滤器title>
head>
<body>
<div id="app">
<App/>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.js">script>
<script>
// 这是个局部组件,非全局组件
let App = {
data() {
return {
msg: 'Hello World!',
time: new Date()
}
},
template: `
{
{ msg|myReverse }}
{
{ time | myTime('YYYY-MM-DD') }}
`,
filters: {
myReverse: function (val) {
console.log(val) // Hello World!
return val.split('').reverse().join('') // 'Hello World!'->['H','e',,,,]->['!','d']->'!dlroW olleH'
},
// 年-月-日(YYYY-MM-DD),年可以不区分大小写,但月日必须区分
myTime: function (val, formatStr) {
return moment(val).format(formatStr)
}
}
}
new Vue({
el: '#app',
data() {
return {
}
},
components: {
App
}
})
script>
html>
全局过滤器:只要过滤器一创建,在任何组件中都能使用。使用的格式(伪代码):
Vue.filter('过滤器的名字',function(val,a,b){
})
//在各个组件中都能使用
数据 | 过滤器的名字('thanlon','erics')
全局过滤器使用示例:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>过滤器title>
head>
<body>
<div id="app">
<App/>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.js">script>
<script>
//全局过滤器,
Vue.filter('myTime', function (val, formatStr) {
return moment(val).format(formatStr);
})
// 这是个局部组件,非全局组件
let App = {
data() {
return {
msg: 'Hello World!',
time: new Date()
}
},
template: `
{
{ msg|myReverse }}
{
{ time | myTime('YYYY-MM-DD') }}
`,
filters: {
myReverse: function (val) {
console.log(val) // Hello World!
return val.split('').reverse().join('') // 'Hello World!'->['H','e',,,,]->['!','d']->'!dlroW olleH'
},
}
}
new Vue({
el: '#app',
data() {
return {
}
},
components: {
App
}
})
script>
html>
过滤器是针对某一个页面的某些个操作,如将后端返回的钱数加上美元符号$或者做其它的处理,如将后端返回的很长的文本进行处理,截取指定长度的字符串,多余的字符串可以拼接上三个点。
开始到结束的过程就是一个生命周期,Vue生命周期的钩子函数主要有以下几种:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、activated、deactivated、beforeDestory、destoryed。
beforeCreate:组件创建之前
created:组件创建之后
beforeMount:装载数据到DOM之前
mounted:装载数据到DOM之后
beforeUpdate:获取原始DOM
updated:获取更新之后的DOM
activated:激活当前组件(vue提供内置组件
)
deactivated:停用当前组件
beforeDestory:组件销毁之前
destoryed:组件销毁之后
生命周期钩子函数.html:
(不建议使用中文命名)
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>生命周期钩子函数title>
head>
<body>
<div id="app">
<App>App>
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
let Test = {
data() {
return {
msg: 'Erics',
count: 0,
timer: null,
}
},
template: `
我的名字是{
{ msg }}!
{
{ count }}
`,
methods: {
changeName() {
this.msg = 'Kiku'
document.getElementById('box').style.color = 'red'
}
},
beforeCreate() {
/**
* 组件创建之前
* beforeCreate获取不到数据
*/
console.log('组件创建之前:', this.msg)
},
created() {
/**
* 组件创建之后,使用该组件就会触发以上钩子方法,created中可以操作数据、可以发送AJAX,并且可以实现数据驱动视图
* 已经有数据了(DOM渲染完成之前),created只是把template创建出来,但是还没有被渲染到app组件中,更没有渲染到Vue中
* Tip:90%的情况下是在created方法中获取数据
* 组件创建之后才可以获取数据
*/
console.log('组件创建之后:', this.msg)
// this.msg = 'Thanlon'
this.timer = setInterval(() => {
this.count++
}, 5000)
/**
*虚拟DOM:由React引出,比原生js和jquery操作DOM效率要有所提高
*/
},
beforeMount() {
/**
* 装载数据到DOM之前会调用
*/
console.log('装载数据到DOM之前会调用:', document.getElementById('app'))
},
mounted() {
/**
* 这个地方可以操作DOM,装载数据到DOM之后(DOM挂载完成)会被调用,可以获取真实存在的DOM元素(标签已经被渲染了)
* Tip:这里可以操作DOM;能用数据驱动改数据就不用操作DOM
*/
console.log('装载数据到DOM之后会调用:', document.getElementById('app'))
},
beforeUpdate() {
/**
* 在更新之前调用,获取原始的DOM
*/
console.log('在更新之前调用:')
console.log(document.getElementById('app').innerHTML);
},
updated() {
/**
* 在更新之后调用,获取嘴型的DOM
*/
console.log('在更新之后调用:')
console.log(document.getElementById('app').innerHTML)
},
beforeDestroy() {
console.log('销毁组件之前调用!')
},
destroyed() {
console.log('销毁组件之后调用!', this.timer) // 每次创建定时器数字是不一样的
// 销毁定时器,销毁和创建很消耗性能
clearInterval(this.timer)
},
activated() {
console.log('组件被激活了!')
},
deactivated() {
console.log('组件被停用了!')
}
}
let App = {
data() {
return {
isShow: true
}
},
/**
template: `
`,
**/
// 这个组件被缓存起来,keep-alive是Vue内置的,其作用是让组件产生缓存
template: `
`,
components: {
Test: Test
},
methods: {
clickHandler() {
this.isShow = !this.isShow
}
}
}
let vm = new Vue({
el: '#app',
data() {
return {
};
},
components: {
App: App
}
})
script>
html>
没有使用 keep-alive 缓存组件:
created 和 mounted 是比较重要的两个钩子函数!created 是在组件创建完成后触发的钩子函数,在函数中可以发起 ajax (XMLHttpRequest,XHR)、axios、fetch、$ajax 请求实现数据驱动视图。mounted 是在 DOM 装载数据之后调用,可以在这里请求数据。组件创建完成后和 DOM 装载数据之后发送获取数据的请求都是没所谓的。获取数据的过程是很快的,大部分是在组件加载之后提前准备好数据。
router的基本使用.html:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>router的基本使用title>
head>
<body>
<div id="app">
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="//unpkg.com/vue-router/dist/vue-router.js">script>
<script>
let Home = {
data() {
return {
}
},
template: `
首页内容!
`
}
let Course = {
data() {
return {
}
},
template: `
免费课程内容!
`
}
const router = new VueRouter({
mode: 'history',
//定义路由规则
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/course',
component: Course
}
]
})
let App = {
data() {
return {
}
},
// router-link和router-view是vue-router提供的两个全局组件,vue-router是路由组件的出口
template: `
首页
免费课程
`,
components: {
Home: Home
}
}
let vm = new Vue({
el: '#app',
// 挂载路由对象(router对象)到Vue的实例中
router: router,
data() {
return {
}
},
template: `
`,
components: {
App: App
}
})
script>
html>
router-link默认被渲染成a标签,to属性会被渲染成href!
命名路由其实就是给路由命名,使用的时候直接使用路由的名字来代替路由。
命名路由.html:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>命名路由title>
head>
<body>
<div id="app">
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="//unpkg.com/vue-router/dist/vue-router.js">script>
<script>
let Home = {
data() {
return {
}
},
template: `
首页内容!
`
}
let Course = {
data() {
return {
}
},
template: `
免费课程内容!
`
}
const router = new VueRouter({
mode: 'history',
//定义路由规则
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/course',
name: 'Course',
component: Course
}
]
})
let App = {
data() {
return {
}
},
// router-link和router-view是vue-router提供的两个全局组件,vue-router是路由组件的出口
template: `
首页
免费课程
`,
components: {
Home: Home
}
}
let vm = new Vue({
el: '#app',
// 挂载路由对象
router: router,
data() {
return {
}
},
template: `
`,
components: {
App: App
}
})
script>
html>
/user/1和/user/2加载的是同一个组件,动态路由匹配示例:
动态路由匹配.html:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>动态路由匹配title>
head>
<body>
<div id="app">
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="//unpkg.com/vue-router/dist/vue-router.js">script>
<script>
let User = {
data() {
return {
user_id: null,
}
},
template: `
我是用户{
{ user_id }}!
`,
created() {
/**
* 当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
* 只调用一次
*/
console.log(this.$route) // 路由信息对象:{name: "User", meta: {…}, path: "/user/1", hash: "", query: {…}, …}
},
watch: {
'$route'(to, from) {
// 对路由变化做出响应
console.log('to:', to) // to: {name: "User", meta: {…}, path: "/user/2", hash: "", query: {…}, …}\
console.log('from:', from) // from: {name: "User", meta: {…}, path: "/user/1", hash: "", query: {…}, …}
console.log(to.params.id)
this.user_id = to.params.id
// 发送AJAX
}
}
}
// 创建路由
const router = new VueRouter({
mode: 'history',
//定义路由规则
routes: [
{
path: '/user/:id',
name: 'User',
component: User
},
]
})
let App = {
data() {
return {
}
},
// router-link和router-view是vue-router提供的两个全局组件,vue-router是路由组件的出口
template: `
用户1
用户2
`,
components: {
User: User
}
}
let vm = new Vue({
el: '#app',
// 挂载路由对象
router: router,
data() {
return {
}
},
template: `
`,
components: {
App: App
}
})
script>
html>
watch可以用来监听路由的变化!
$.route是路由信息对象,$router是路由对象VueRouter!
上面所有部分使用的是声明式导航:
<router-link :to="{name:'User',params:{id:1}}">用户1router-link>
<router-link :to="{name:'User',params:{id:2}}">用户2router-link>
编程式导航:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>编程式导航title>
head>
<body>
<div id="app">
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="//unpkg.com/vue-router/dist/vue-router.js">script>
<script>
let Home = {
data() {
return {
}
},
template: `
我是首页
`
}
let User = {
data() {
return {
user_id: null,
}
},
template: `
User{
{ user_id }}!
`,
created() {
/**
* 当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
* 只调用一次
*/
console.log(this.$route) // 路由信息对象:{name: "User", meta: {…}, path: "/user/1", hash: "", query: {…}, …}
},
watch: {
'$route'(to, from) {
// 对路由变化做出响应
console.log('to:', to) // to: {name: "User", meta: {…}, path: "/user/2", hash: "", query: {…}, …}\
console.log('from:', from) // from: {name: "User", meta: {…}, path: "/user/1", hash: "", query: {…}, …}
console.log(to.params.id)
this.user_id = to.params.id
// 发送AJAX
}
},
methods: {
// 编程式导航
clickHandler() {
this.$router.push({
name: 'Home'
})
}
}
}
// 创建路由
const router = new VueRouter({
mode: 'history',
//定义路由规则
routes: [
{
path: '/home',
name: 'Home',
component: Home
},
{
path: '/user/:id',
name: 'User',
component: User
},
]
})
let App = {
data() {
return {
}
},
// router-link和router-view是vue-router提供的两个全局组件,vue-router是路由组件的出口
template: `
用户1
用户2
`,
components: {
User: User
}
}
let vm = new Vue({
el: '#app',
// 挂载路由对象
router: router,
data() {
return {
}
},
template: `
`,
components: {
App: App
}
})
script>
html>
refs属性可以用来获取标签原生的DOM对象和组件实例,需要在mounted函数中获取。
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>refs属性的使用title>
head>
<body>
<div id="app">
div>
body>
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
Vue.component('Test', {
data() {
return {
}
},
template: `
我是测试组件1!
`
})
Vue.component('Test2', {
data() {
return {
}
},
template: `
我是测试组件2!
`
})
let App = {
data() {
return {
}
},
template: `
`,
mounted() {
console.log(this.$refs.input) // 获取原始DOM对象:
console.log(this.$refs) // {input: input}...可以展开
this.$refs.input.focus()
console.log(this) // VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
console.log(this.$root) // VueComponent {_uid: 1, _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.parent) // undefined
console.log(this.$refs.root) // undefined
console.log(this.$children[0]) // VueComponent {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
}
}
let vm = new Vue({
el: '#app',
data() {
return {
}
},
template: `
`,
components: {
App
}
})
script>
html>
使用node来运行模块化的JavaScript程序:
test.js:
let person = {
name: 'erics'
}
module.exports = person
index.js:
let person = require('./test')
console.log(person)
console.log(person.name)
可以使用 node index.js
命令来运行,
$ node index.js
{
name: 'erics' }
erics
使用webpack打包:
module.js:
let person = {
name: 'erics',
func: function () {
alert('success!')
}
}
export default person;
main.js:
import person from './module'
console.log(person.func())
使用webpack进行打包:
$ sudo webpack ./main.js -o ./bundle.js
Hash: b7b9dbe6291ed213470d
Version: webpack 4.44.1
Time: 168ms
Built at: 2020/08/21 下午1:52:26
Asset Size Chunks Chunk Names
bundle.js 1 KiB 0 [emitted] main
Entrypoint main = bundle.js
[0] ./main.js + 1 modules 170 bytes {
0} [built]
| ./main.js 56 bytes [built]
| ./module.js 114 bytes [built]
打包完成后当前目录中多了一个bundle.js文件,这就是打包后的文件。在index.html中只需要引入它:
index.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
head>
<body>
body>
<script src="bundle.js">script>
html>
main.js和module.js里面的代码成功被执行:
可以抛出多个变量,修改model.js和main.js重新打包,
module.js:
let person = {
name: 'erics',
func: function () {
alert('success!')
}
}
let name = 23
// 可以抛出多个变量
export {
name}
export let nowYear = 2020
export default person;
main.js:
import * as a from './module'
console.log(a)
webpack依赖于node.js!
可以把模块化应用在Vue上,
App.js:
let App = {
template: `
我是App组件!
`
}
export default App;
main.js:
import Vue from './vue'
import App from './App'
let vm = new Vue({
el: '#app',
template: `
`,
components: {
App
}
})
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="./bundle.js"></script>
</html>
使用webpack命令打包:webpack main.js -o bundle.js
,然后可以正常访问 index.html
。
很明显上面的例子中每次更新 JavaScript 代码都会重新打包,这是很繁琐的。实际上,通过设置完全可以自动打包,实时编译
。首先需要通过 npm -init --yes
命令来创建配置文件 package.json
:
{
"name": "webpack_use",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
接下来通过 $ cnpm i webpack -D
下载 webpack
。
可以指定webpack的版本:$ cnpm i [email protected] -D
下载完成后配置文件 package.json
中发生了变化:
"devDependencies": {
"webpack": "^4.44.1"
}
devDependencies 指的是开发环境依赖。可以对 package.json
进行修改,使用 npm 命令 $ npm run dev
来替代 $ webpack ./main.js -o ./bundle.js
打包文件。所以,还需要修改配置文件:
"scripts": {
"dev": "webpack ./main.js ./bundle.js"
},
还可以进行改进,修改 package.json
:
"scripts": {
"dev": "webpack"
},
我们想要只使用 webpack 命令来打包,可以在项目中创建一个 webpack 默认的配置文件 webpack.config.js
,该配置文件写的都是 nodejs 代码:
module.exports = {
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
}
}
使用 $npm run dev
会优先找配置文件 webpack.config.js
,找到入口 main.js
,打包输出 bundle.js
。为了实现 监听代码的改动自动编译
,需要加上 watch:true
:
module.exports = {
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
},
watch: true
}
开发环境中需要有监听代码自动编译功能,生产环境不需要,
webpack.dev.config.js:
module.exports = {
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
},
watch: true
}
webpack.pro.config.js:
module.exports = {
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
}
}
package.json:
{
"name": "webpack_use",
"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": {
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
}
}
如果是新版本的 webpack,可以这样配置,
webpack.pro.config.js:
module.exports = {
// entry: './main.js',
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
},
watch: true,
mode: 'development'
}
package.json:
{
"name": "webpack_use",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
}
}
可以把css直接打包到js文件中,不需要再html文件中再引用。新建 index.css
文件,在里面写好样式,然后导入到 main.js
中,
index.css:
body{
background: rebeccapurple;
}
main.js:
import Vue from './vue'
import App from './App'
import './index.css'
let vm = new Vue({
el: '#app',
template: `
`,
components: {
App
}
})
但是会报错:
ERROR in ./index.css 1:4
Module parse failed: Unexpected token (1:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> body{
| background: rebeccapurple;
| }
@ ./main.js 3:0-20
在组件中引入css,需要用到css-loader和style-loader, css-loader是用来解析css文件,style-loader是用来解析style标签的。所以需要下载并配置它们:$ cnpm i css-loader style-loader -D
,
webpack.config.js:
module.exports = {
// entry: './main.js',
entry: {
'main': './main.js'
},
output: {
'filename': './bundle.js'
},
watch: true,
mode: 'development',
module: {
rules: [
{
/**
* 遇到后缀是css的文件,webpack首先用css-loader加载器去解析这个文件
* 最后计算完的css,将会使用style-loader生成一个内容为最终解释完的css代码的style标签,放在head标签里
* webpack在打包国城中,遇到后缀为css的文件,就会使用style-loader和css-loader去加载这个文件
*/
test: /\.css$/, // 后缀是css的文件
loader: 'style-loader!css-loader', // 先去使用css-loader解析css文件再使用style-loader生成style标签(link)插入head标签中
}
]
}
}
这个时候css就会被加入到html中。
一般也会把 index.html
文件自动生成到 dist
文件中,所以需要使用到 html-webpack-plugin
插件。安装该插件:$ cnpm i html-webpack-plugin -D
,指定版本需要加上@跟版本号。另外一个命令是 $ cnpm i html-webpack-plugin --save-dev
。还需要在 webpack.config.js
进行配置:
const path = require('path')
// 导入模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// entry: './main.js',
entry: {
// 可以有多个入口,也可以有一个,如果有一个默认从这个入口开始分析
'main': './src/main.js'
},
output: {
// 指定产出目录
path: path.resolve('./dist'), // 相对转绝对路径,dist也可以改成static,webpack默认使用dist
'filename': 'build.js'
},
watch: true, // 文件监视改动,自动产生build.js
mode: 'development',
module: {
rules: [
{
/**
* 遇到后缀是css的文件,webpack首先用css-loader加载器去解析这个文件
* 最后计算完的css,将会使用style-loader生成一个内容为最终解释完的css代码的style标签,放在head标签里
* webpack在打包国城中,遇到后缀为css的文件,就会使用style-loader和css-loader去加载这个文件
*/
test: /\.css$/, // 后缀是css的文件
use: ["style-loader", 'css-loader',], // 先去使用css-loader解析css文件再使用style-loader生成style标签(link)插入head标签中
}
]
},
// 添加插件
plugins: [
new HtmlWebpackPlugin({
// 插件的执行运行与元素索引有关
template: './src/index.html', //参照物
})
]
}
生成之前的项目目录结构:
执行 $ npm run dev
执行生成之后的目录结构:
index.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack中plugin的使用title>
head>
<body>
<div id="app">div>
<script src="build.js">script>
body>
<script src="dist/build.js">script>
html>
使用 $ cnpm i vue-loader vue-template-compiler -D
下载用于解析 Vue文件的包 vue-loader
和 vue-template-compiler
。创建 App.vue
文件:
<!--组件的模板结构-->
<template>
<div>
App组件
</div>
</template>
<!--组件的业务逻辑-->
<script>
export default {
name:'App',
data() {
return {
text: 'Hello fingle file...'
}
}
}
</script>
<!--组件的样式-->
<style>
body {
background-color: rebeccapurple;
}
</style>
需要在 webpack.config.js
中进行配置:
const path = require('path')
// 导入模块
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
// entry: './main.js',
entry: {
// 可以有多个入口,也可以有一个,如果有一个默认从这个入口开始分析
'main': './src/main.js'
},
output: {
// 指定产出目录
path: path.resolve('./dist'), // 相对转绝对路径,dist也可以改成static,webpack默认使用dist
'filename': 'build.js'
},
watch: true, // 文件监视改动,自动产生build.js
mode: 'development',
module: {
// 这个节点用于配置所有的第三方模块加载器
rules: [
{
/**
* 遇到后缀是css的文件,webpack首先用css-loader加载器去解析这个文件
* 最后计算完的css,将会使用style-loader生成一个内容为最终解释完的css代码的style标签,放在head标签里
* webpack在打包国城中,遇到后缀为css的文件,就会使用style-loader和css-loader去加载这个文件
*/
test: /\.css$/, // 后缀是css的文件
use: ["style-loader", 'css-loader',], // 先去使用css-loader解析css文件再使用style-loader生成style标签(link)插入head标签中
},
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
// 添加插件
plugins: [
new HtmlWebpackPlugin({
// 插件的执行运行与元素索引有关
template: './index.html', //参照物
}),
new VueLoaderPlugin()
]
}
还要记得修改 main.js
:
import Vue from './vue'
import App from './App.vue'
import './index.css'
let vm = new Vue({
el: '#app',
template: ` `,
components: {
App
}
})
执行 $ npm run dev
命令正常打包即可!
使用 $ cnpm i vue vue-router -S
安装 vue
和 vue-router
模块,-S 表示当前项目依赖,查看 package.json
:
{
"name": "webpack_use",
"version": "1.0.0",
"description": "",
"main": "src/main.js",
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^4.2.1",
"html-webpack-plugin": "^4.3.0",
"style-loader": "^1.2.1",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"dependencies": {
"vue": "^2.6.12",
"vue-router": "^3.4.3"
}
}
配置路由:main.js
import Vue from './vue'
import App from './App.vue'
import './index.css'
import VueRouter from 'vue-router'
import Home from './components/Home/Home'
import Course from './components/Course/Course'
//创建路由对象
const router = new VueRouter({
// 配置路由信息
routers: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/course',
name: 'Course',
component: Course
},
]
})
let vm = new Vue({
el: '#app',
router: router,
template: `
`,
components: {
App
}
})
常用配置相关参数:
--open:自动打开浏览器
--hot:热更新,不在刷新的情况下替换css样式
--inline:自动刷新
--port 9000:指定端口
--process:显示编译进度
希望可以在服务器上运行,下载模块:$ cnpm i webpack-dev-server --save-dev
,查看 package.json
:
{
"name": "webpack_use",
"version": "1.0.0",
"description": "",
"main": "src/main.js",
"scripts": {
"dev": "webpack-dev-server --open --hot --inline --config ./webpack.config.js",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^4.2.1",
"html-webpack-plugin": "^4.3.0",
"style-loader": "^1.2.1",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"vue": "^2.6.12",
"vue-router": "^3.4.3"
}
}
然后执行 $ npm run dev
,自动打开了浏览器但是编译脚本报错:
找不到模块,Course.vue 和 Home.vue,排查后发现之前声明组件的时候 Home.vue
和 Course.vue
书写有误,修改 main.js
:
import Vue from './vue'
// 一定要以vue结尾
import App from './App.vue'
import './index.css'
import VueRouter from 'vue-router'
// 如果基于模块化机制:Vue.use(VueRouter)
// 声明组件
import Home from './components/Home/Home.vue'
import Course from './components/Course/Course.vue'
//创建路由对象
const router = new VueRouter({
// 配置路由信息
routers: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/course',
name: 'Course',
component: Course
},
]
})
let vm = new Vue({
el: '#app',
router: router,
template: `
`,
components: {
App
}
})
这个时候就可以正常编译了,编译后遇到下面的问题:
需要在 main.js
中加上 Vue.use(VueRouter)
,
import Vue from './vue'
// 一定要以vue结尾
import App from './App.vue'
import './index.css'
import VueRouter from 'vue-router'
// 基于模块化机制
Vue.use(VueRouter)
// 声明组件
import Home from './components/Home/Home.vue'
import Course from './components/Course/Course.vue'
//创建路由对象
const router = new VueRouter({
// 配置路由信息
routers: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/course',
name: 'Course',
component: Course
},
]
})
let vm = new Vue({
el: '#app',
router: router,
template: ` `,
components: {
App
}
})
这里可以把 main.js
中的Vue的部分修改为:
let vm = new Vue({
el: '#app',
router: router,
render: c => c(App)
// template: ` `,
// components: {
// App
// }
})
Github:https://github.com/ThanlonSmith/SPA-Vue
scoped可以使当前样式只在当前组件生效,如,
Course.vue:
<template>
<div>
课程列表!
</div>
</template>
<!--组件的业务逻辑-->
<script>
export default {
name: 'Course',
data() {
return {
}
},
}
</script>
<!--组件的样式-->
<style scoped>
* {
color: orange;
}
</style>
Home.vue:
<template>
<div>
首页!
</div>
</template>
<!--组件的业务逻辑-->
<script>
export default {
name: 'Home',
data() {
return {
}
},
}
</script>
<!--组件的样式-->
<style scoped>
*{
color: red;
}
</style>
class重复也是没有关系的!
可使用的模块有:
使用脚手架创建一个基于 webpack-simple
模块的项目:
$ vue init webpack-simple my-project
? Project name my-project
? Project description A Vue.js project
? Author erics1996 <erics1996@yeah.net>
? License MIT
? Use sass? No
进入目录,安装需要的相关依赖包:
$ cd my-project
$ cnpm install
$ npm run dev
使用 $ cnpm i vue-router
下载路由,可以在src目录下创建router文件夹和index.js用来写路由相关信息:
将路由对象VueRouter 挂在到 Vue 实例,main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js' // 可以不用加js后缀,路由相关的信息在index.js中写
new Vue({
router,
el: '#app',
render: h => h(App)
})
创建router目录用于存放路由相关的信息,创建componets用来存放组件:
index.js:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from "../components/Home/Home";
import Course from "../components/Course/Course";
Vue.use(VueRouter) // use相当于挂在父类
const router = new VueRouter({
// 配置路由信息
routes: [ // 这里不要写错了
{
path: '/',
redirect: '/home'
},
{
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',
data() {
return {
}
},
}
</script>
<style></style>
Course.vue:
<template>
<div>我是课程列表页面!</div>
</template>
<script>
export default {
name: 'Course',
data() {
return {
}
},
}
</script>
<style></style>
App.vue:
<template>
<!-- <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>-->
<div id="app">
<img src="./assets/logo.png">
<h4>{
{
msg }}</h4>
<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: '基于webpack-simple模板构建!'
}
}
}
</script>
<style></style>
构建好的**工程目录结构:
同样使用 $ npm run dev
命令来运行:
默认找 index.js
,可以不写后缀:
Vue.config.productionTip = false
的意思是生产环境不需要控制台有提示,@符号默认是 src
路径。
使用 $ cnpm i element-ui -S
安装 element-ui,使用 $ cnpm i
安装 element-ui 依赖的包,使用 element-ui 来布局 App.vue:
<template>
<div id="app">
<el-container>
<el-header>
<router-link :to="{name:'Home'}">首页</router-link>
<router-link :to="{name:'Course',query:{userid:1}}">免费课程</router-link>
</el-header>
<el-main>
<router-view/>
</el-main>
</el-container>
</div>
</template>
<script>
export default {
name: '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;
}
</style>
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' // 默认找index.js
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import '../static/global/index.css'
Vue.config.productionTip = false // 生产环境不需要控制台有提示
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: {
App},
template: ' '
})