本文共 14474 字,大约阅读时间需要 48 分钟。
GitHub源码 :
yarn init -y
console.log('Hello Webpack!')document.getElementById('root').innerHTML = 'Hello222
'
yarn add -D webpack webpack-cliyarn add -D html-webpack-plugin
const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { // 模式: 生产环境 mode: 'production', // 入口 entry: { app: path.resolve(__dirname, 'src/index.js') }, // 出口(打包生成js) output: { filename: 'static/js/[name].bundle.js', path: path.resolve(__dirname, 'dist') }, // 模块加载器 module: { rules: [ ] }, // 插件 plugins: [ new HtmlWebpackPlugin({ template: 'index.html', filename: 'index.html' }) ] }
配置打包命令: "build": "webpack --mode production"打包项目: yarn build运行打包项目: serve dist
每次修改项目代码后, 必须重新打包, 重新运行
yarn add -D webpack-dev-server
devServer: { open: true, // 自动打开浏览器 quiet: true, // 不做太多日志输出}
devtool: 'cheap-module-eval-source-map'
配置命令: "dev": "webpack-dev-server --mode development"执行命令: yarn dev
a. 下载依赖包 yarn add -D babel-loader @babel/core @babel/preset-envb. 配置{ test: /\.js$/, //exclude: /(node_modules|bower_components)/, include: path.resolve(__dirname, 'src'), use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } }}
a. 下载依赖包 yarn add -D css-loader style-loaderb. 配置{ test: /\.css$/, use: ['style-loader', 'css-loader'], // 多个loader从右到左处理}
a. 下载依赖包 yarn add -D url-loader@2.3.0 file-loader@4.3.0 b. 配置 { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 1000, name: 'static/img/[name].[hash:7].[ext]' // 相对于output.path } }
a. 添加图片: src/assets/imgs/logo.pngb. 添加css: src/assets/css/my.cssimg { width: 200px; height: 200px;}c. index.jsimport logo from './assets/imgs/logo.png'import './assets/css/my.css'const image = new Image()image.src = logodocument.body.appendChild(image)document.getElementById('root').innerHTML = 'Hello222
'
1). 文档: https://vue-loader.vuejs.org/zh/2). 下载依赖包: yarn add vue yarn add -D vue-loader vue-template-compiler3). 配置 const VueLoaderPlugin = require('vue-loader/lib/plugin') { test: /\.vue$/, include: path.resolve(__dirname, 'src'), loader: 'vue-loader' } { test: /\.css$/, use: ['vue-style-loader', 'css-loader'], } new VueLoaderPlugin() // 引入模块的解析 resolve: { extensions: ['.js', '.vue', '.json'], // 可以省略的后缀名 alias: { // 路径别名(简写方式) 'vue$': 'vue/dist/vue.esm.js', // 表示精准匹配 } }4). 编码: src/App.vue src/index.js
webpack-dev-server内部利用http-proxy-middle包对特定请求进行转发操作
devServer: { proxy: { // 处理以/api开头路径的请求 // '/api': 'http://localhost:4000' '/api': { target: 'http://localhost:4000', // 转发的目标地址 pathRewrite: { '^/api' : '' // 转发请求时去除路径前面的/api }, changeOrigin: true, // 支持跨域 } }}
1). 利用webpack-dev-server进行请求代理转发 webpack-dev-server内部利用http-proxy-middle包对特定请求进行转发操作2). 配置: devServer: { proxy: { // 处理以/api开头路径的请求 // '/api': 'http://localhost:4000' '/api': { target: 'http://localhost:4000', // 转发的目标地址 pathRewrite: { '^/api' : '' // 转发请求时去除路径前面的/api }, changeOrigin: true, // 支持跨域 } } }
yarn add @babel/runtime-corejs2
presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', 'corejs': 2 // 处理一些新语法的实现 }]]
"plugins": [ ["component", [ { "libraryName": "mint-ui", "style": true } ]]]
Error: .plugins[0][1] must be an object, false, or undefined
文档编写时, 是根据老的babel版本编写的, 新版本的babel配置有变化以前是数组, 现在只能是对象
"plugins": [ ["component", { "libraryName": "mint-ui", "style": true }]]
devServer: historyApiFallback: true, // 任意的 404 响应都被替代为 index.htmloutput: publicPath: '/', // 引入打包的文件时路径以/开头
xxx
拆分界面, 抽取组件
编写静态组件
编写动态组件
设计data
类型: [{id: 1, title: ‘xxx’, completed: false}] 名称: todos 位置: 如果只是哪个组件用, 交给它, 如果是哪些组件用, 交给共同的父组件关于状态数据的更新
data数据定义在哪个组件, 更新数据的行为就定义在哪个组件 如果子组件要更新父组件的数据, 调用父组件的更新函数来更新父组件的数据 一个组件接收属性数据不要直接修改, 只是用来读取显示的props
vue的自定义事件 全局事件总线 slot vuex
父子组件间通信的基本方式属性值的2大类型: 一般/非函数: 父组件-->子组件 函数: 子组件-->父组件问题: 隔层组件间传递: 必须逐层传递(麻烦) 兄弟组件间: 必须借助父组件(麻烦)
注意
: 标签是在父组件中解析
xxx
vue-resource
vue 插件,非官方库, vue1.x 使用广泛
**在线文档:**https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
axios
通用的ajax请求库,官方推荐, vue2.x 使用广泛
**在线文档:**https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
安装插件:
// yarn 安装yarn add vue-resource axios// nmp 安装npm install vue-resource --savenpm install axios --save
案例效果
vue-resource:
编码:
index.js
import Vue from 'vue'import VueResource from 'vue-resource' import APP from './APP.vue' // 引入自定义组件Vue.config.productionTip = false;// 声明使用Vue插件Vue.use(VueResource); // 内部给所有的组件对象都添加一个属性对象: $httpnew Vue({ el: '#root', render: h => h(APP),});
APP.vue
loading...
most star repo is { {repoName}}
axios
index.js
import Vue from 'vue'import APP from './APP.vue' // 引入自定义组件Vue.config.productionTip = false;new Vue({ el: '#root', render: h => h(APP),});
APP.vue
loading...
most star repo is { {repoName}}
编码:
server.js
/* 后台服务器应用模块: 使用express快速搭建后台路由 */const express = require('express');const axios = require('axios');const app = express();// 注册后台路由(转发请求)app.get('/repositories/:q', (rep, res) =>{ const q = rep.params.q; axios({ method: 'GET', url: 'https://api.github.com/search/repositories', params: { q, sort: 'stars' } }).then(response =>{ const { name, html_url} = response.data.items[0]; res.send({ status: 0, data: { name, html_url} }); });});app.listen('4000', () =>{ console.log("server listen on http://localhost:4000");})
测试:
启动express: node server.js
vuex管理的状态对象它应该是唯一的const state = { xxx: initValue}
包含多个直接更新state的方法(回调函数)的对象谁来触发: action中的commit('mutation名称')只能包含同步的代码, 不能写异步代码const mutations = { yyy (state, {data1}) { // 更新state的某个属性 }}
包含多个事件回调函数的对象通过执行: commit()来触发mutation的调用, 间接更新state谁来触发: 组件中: $store.dispatch('action名称', data1) // 'zzz'可以包含异步代码(定时器, ajax)const actions = { zzz ({commit, state}, data1) { commit('yyy', {data1}) }}
包含多个计算属性(getter)的对象谁来读取: 组件中: $store.getters.xxxconst getters = { mmm (state) { return ... }}
包含多个module的对象一个module是一个包含state/mutations/actions/getters的对象是将一复杂应用的vuex代码进行多模块拆分的第2种方式
vuex的核心管理对象, 是组件与vuex通信的中间人读取数据的属性 state: 包含最新状态数据的对象 getters: 包含getter计算属性的对象更新数据的方法 dispatch(): 分发调用action commit(): 提交调用mutation
export default new Vuex.Store({ state, mutations, actions, getters, modules: { a, b }})
import store from './store'new Vue({ store})
import {mapState, mapGetters} from 'vuex'export default { computed: ( ...mapState(['xxx']), ...mapGetters(['yyy']) ) methods: { test () { this.$store.dispatch('zzz', data) this.$store.commit('zzz', data) } }}
1). 创建路由器: router/index.js new VueRouter({ mode: 'hash/history' routes: [ { // 一般路由 path: '/about', component: About }, { // 自动跳转路由 path: '/', redirect: '/about' } ] })2). 注册路由器: main.js import router from './router' new Vue({ router })3). 使用路由组件标签:Go to XXX // 可以不使用// 必须使用4). 2个对象 $router: 代表路由器对象, 包含一些实现路由跳转/导航的方法: push()/replace()/back() $route: 代表当前路由对象, 包含一些路由相关的属性: path/params/query/meta
定义路由组件
映射路由
使用显示当前路由组件
children: [ { path: '/home/news/:id/:title', component: news }, { path: 'message', component: message }]
params/query:将请求参数映射成props: props=true | props: route => ({id: route.params.id})变相props:
注册路由: { name: 'news' path: '/home/news/:id/:title', component: News }跳转:router.push({name: 'news', params: {id: 1, title: 'abc'}})
路由组件对象默认的生命周期: 被切换时就会死亡, 切换回来时重新创建
this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)this.$router.back(): 请求(返回)上一个记录路由
hash模式: 路径中带#: http://localhost:8080/#/home/news 发请求的路径: http://localhost:8080 项目根路径 响应: 返回的总是index页面 ==> path部分(/home/news)被解析为前台路由路径history模式: 路径中不带#: http://localhost:8080/home/news 发请求的路径: http://localhost:8080/home/news 响应: 404错误 希望: 如果没有对应的资源, 返回index页面, path部分(/home/news)被解析为前台路由路径 解决: 添加配置 devServer: historyApiFallback: true, // 任意的 404 响应都被替代为 index.html output: publicPath: '/', // 引入打包的文件时路径以/开头
调试的目的
查找bug: 不断缩小可疑代码的范围
查看程序的运行流程(用于熟悉新接手项目的代码)
如何开启调试模式
添加debugger语句: 程序运行前 此方式用打包后才运行的项目
添加(打)断点: 程序运行前或者过程中 此方式用运行源码js
如何进行调试操作
resume: 恢复程序执行(可能执行完或者进入下一个断点处)
step over: 单步跳转, 尝试执行完当前语句, 进入下一条(如果内部有断点, 自动进入内部断点处) step into: 跳入, 进入当前调用函数内部 step out: 跳出, 一次性执行完当前函数后面所有语句,并出去 deactivate breakpoints: 使所有断点暂时失效call stack: 显示是程序函数调用的过程
scope: 当前执行环境对应的作用域中包含的变量数据
breakpoints: 断点列表
通过一个对象代理对另一个对象中属性的操作(读/写)
通过vm对象来代理data对象中所有属性的操作
好处: 更方便的操作data中的数据
基本实现流程
1.模板解析的关键对象: compile对象
2.模板解析的基本流程:3.解析插值语法节点: textNode.textContent = value
4.事件指令解析: elementNode.addEventListener(‘eventName’, callback.bind(vm))
5.一般指令解析: elementNode.xxx = value
数据绑定(model==>View)
一旦更新了data中的某个属性数据, 所有界面上直接使用或间接使用了此属性的节点都会更新(更新)
数据劫持
四个重要对象
Observer
用来对data所有属性数据进行劫持的构造函数
Dep(Depend)
data中的每个属性(所有层次)都对应一个dep对象
创建的时机:
在初始化define data中各个属性时创建对应的dep对象
在data中的某个属性值被设置为新的对象时对象的结构{ id, // 每个dep都有一个唯一的id subs //包含n个对应watcher的数组(subscribes的简写)}
subs属性说明
当一个watcher被创建时, 内部会将当前watcher对象添加到对应的dep对象的subs中
当此data属性的值发生改变时, 所有subs中的watcher都会收到更新的通知, 从而最终更新对应的界面
Compile
Watcher
{ vm, //vm对象 exp, //对应指令的表达式 cb, //当表达式所对应的数据发生改变的回调函数 value, //表达式当前的值 depIds //表达式中各级属性所对应的dep对象的集合对象 //属性名为dep的id, 属性值为dep}
总结: dep与watcher的关系: 多对多
双向数据绑定
npm install -g @vue/cli# ORyarn global add @vue/cli
脚手架v3
npm install -g @vue/cli# 创建一个项目vue create vue-app3
脚手架v2
Vue CLI >= 3 和旧版使用了相同的 vue
命令,所以 Vue CLI 2 (vue-cli
) 被覆盖了。如果你仍然需要使用旧版本的 vue init
功能,你可以全局安装一个桥接工具:
npm install -g @vue/cli-init# `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同vue init webpack my-project
v2的配置是直接可见, v3是包装隐藏起来了
修改配置: v2是直接在配置文件中修改, v3提供了一个专门的配置: vue.config.js, 我们可以根据文档在此文件中添加配置
depIds //表达式中各级属性所对应的dep对象的集合对象
//属性名为dep的id, 属性值为dep }>总结: dep与watcher的关系: 多对多* 一个data中的属性对应对应一个dep, 一个dep中可能包含多个watcher(模板中有几个表达式使用到了属性)* 模板中一个非事件表达式对应一个watcher, 一个watcher中可能包含多个dep(表达式中包含了几个data属性)* 数据绑定使用到2个核心技术 * **defineProperty()** * 订阅者-发布者>**双向数据绑定**1. 双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的2. 双向数据绑定的实现流程: * 在解析v-model指令时, 给当前元素添加input监听 * 当input的value发生改变时, 将最新的值赋值给当前表达式所对应的data属性# Vue CLI## 一、安装~~~shellnpm install -g @vue/cli# ORyarn global add @vue/cli
脚手架v3
npm install -g @vue/cli# 创建一个项目vue create vue-app3
脚手架v2
Vue CLI >= 3 和旧版使用了相同的 vue
命令,所以 Vue CLI 2 (vue-cli
) 被覆盖了。如果你仍然需要使用旧版本的 vue init
功能,你可以全局安装一个桥接工具:
npm install -g @vue/cli-init# `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同vue init webpack my-project
转载地址:http://vabki.baihongyu.com/