Github 源码 :https://github.com/OYCodeSite/VueCode.git
yarn init -y
console.log('Hello Webpack!')
document.getElementById('root').innerHTML = '<h1>Hello222</h1>'
<div id="root"></div>
yarn add -D webpack webpack-cli
yarn 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-env
b. 配置
{
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-loader
b. 配置
{
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.png
b. 添加css: src/assets/css/my.css
img {
width: 200px;
height: 200px;
}
c. index.js
import logo from './assets/imgs/logo.png'
import './assets/css/my.css'
const image = new Image()
image.src = logo
document.body.appendChild(image)
document.getElementById('root').innerHTML = '<h1>Hello222</h1>'
1). 文档:
https://vue-loader.vuejs.org/zh/
2). 下载依赖包:
yarn add vue
yarn add -D vue-loader vue-template-compiler
3). 配置
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.html
output: publicPath: '/', // 引入打包的文件时路径以/开头
<template>
<div>xxx</div>
</template>
<script>
export default {
props: []/{}
data(){},
computed: {}
methods: {},
watch: {}
filters: {}
directives: {}
components: {}
}
</script>
<style scoped></style>
props vue 的自定义事件 全局事件总线 slot vuex
父子组件间通信的基本方式
属性值的2大类型:
一般/非函数: 父组件-->子组件
函数: 子组件-->父组件
问题:
隔层组件间传递: 必须逐层传递(麻烦)
兄弟组件间: 必须借助父组件(麻烦)
注意
: ==标签是在父组件中解析==
<template>
<div>xxx</div>
</template>
<script>
export default {
props: []/{}
data(){},
computed: {}
methods: {},
watch: {}
filters: {}
directives: {}
components: {}
}
</script>
<style scoped></style>
安装插件:
// yarn 安装
yarn add vue-resource axios
// nmp 安装
npm install vue-resource --save
npm 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); // 内部给所有的组件对象都添加一个属性对象: $http
new Vue({
el: "#root",
render: (h) => h(APP),
});
APP.vue
<template>
<div>
<p v-if="!repoName">loading...</p>
<p v-else>
most star repo is
<a :href="repoUrl">{{ repoName }}</a>
</p>
</div>
</template>
<script type="text/ecmascript-6">
export default {
data(){
return{
repoName : '',
repoUrl: ''
}
},
mounted(){
// 利用vue-resource发送ajax请求获取数据
this.$http.get('https://api.github.com/search/repositories?q=v&sort=stars')
.then(response =>{
const result = response.data;
const {name,html_url} = result.items[0];
this.repoName = name;
this.repoUrl = html_url;
})
.catch(error =>{
alert('请求出错了');
});
},
}
</script>
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
<template>
<div>
<p v-if="!repoName">loading...</p>
<p v-else>
most star repo is
<a :href="repoUrl">{{ repoName }}</a>
</p>
</div>
</template>
<script type="text/ecmascript-6">
import axios from 'axios'
export default {
data(){
return{
repoName : '',
repoUrl: ''
}
},
mounted(){
// 利用axious 发送ajax 请求获取数据
axios.get('https://api.github.com/search/repositories',{
params: {
q : 'v',
sort: 'stars'
}
})
.then(response =>{
const result = response.data;
const {name, html_url} = result.items[0];
this.repoName = name;
this.repoUrl = html_url;
})
.catch(error =>{
alert('请求出错了');
})
},
}
</script>
编码:
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.xxx const
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). 使用路由组件标签:
<router-link to="/xxx">Go to XXX</router-link> // 可以不使用
<router-view></router-view> // 必须使用
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: <router-link to="/home/news/123/abc?zzz=1234">
将请求参数映射成props: props=true | props: route => ({id: route.params.id})
变相props: <router-view msg='abc'>
注册路由:
{
name: 'news'
path: '/home/news/:id/:title',
component: News
}
跳转:
<router-link to="{name: 'news', params: {id: 1, title: 'abc'}}">
router.push({name: 'news', params: {id: 1, title: 'abc'}})
路由组件对象默认的生命周期: 被切换时就会死亡, 切换回来时重新创建
<keep-alive exlude="A,B">
<router-view></router-view>
</keep-alive>
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: '/', // 引入打包的文件时路径以/开头
1.模板解析的关键对象: compile 对象 2.模板解析的基本流程:
Observer
Dep(Depend)
data 中的每个属性(所有层次)都对应一个 dep 对象
Compile
Watcher
{ vm, //vm对象 exp, //对应指令的表达式 cb,
//当表达式所对应的数据发生改变的回调函数 value, //表达式当前的值 depIds
//表达式中各级属性所对应的dep对象的集合对象 //属性名为dep的id, 属性值为dep }
总结: dep 与 watcher 的关系: 多对多
双向数据绑定
npm install -g @vue/cli
# OR
yarn 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