- {{ item }}
const htmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// 打包提取css
const ExtractTextPlugin = require("extract-text-webpack-plugin");
// 混淆解压代码
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
//提取公共模块,其的依赖
const webpack = require('webpack');
const path = require('path')
module.exports = {
optimization: {
minimizer: [new UglifyJsPlugin()],
},
entry: { //main是默认入口,也可以是多入口
main: './src/main.js',
},
//出口
output: {
// filename:'./build.js?v=1.1.1', //指定js文件
filename: 'js/[name].[chunkhash:8].js', //指定js文件名
path: path.join(__dirname, 'dist'), //最好是绝对路径(网站根目录) --打包文件夹名
publicPath: '/', // 企业中也可以是www.***.com/ 所有资源请求以/(网站根目录)开头
},
module: {
//一样的功能rules: webpack2.x之后新加的
rules: [ //require('./a.css||./a.js')
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.(png|jpg|gif|svg|ttf|woff|woff2)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
name: 'assets/[name].[ext]'
},
},
],
}, {//处理ES6的js
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.vue$/,
use: 'vue-loader',
exclude: /node_modules/
},
]
},
plugins: [
// 打包提取css 打包名字[] 唯一标识
new ExtractTextPlugin("css/[name].[hash:8].css"),
//插件的执行顺序是依次执行的
new htmlWebpackPlugin({
template: './src/index.html',
inject: true,
favicon: './favicon.ico'
}),
new VueLoaderPlugin()
//将src下的template属性描述的文件根据当前配置的output.path,将文件移动到该目录
],
// 提取公共组件及第三方包
optimization: {
//包清单
runtimeChunk: {
name: "manifest"
},
//拆分公共包
splitChunks: {
cacheGroups: {
//项目公共组件
common: {
chunks: "initial",
name: "common",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
//第三方组件
vendor: {
test: /node_modules/,
chunks: "initial",
name: "vendor",
priority: 10,
enforce: true
}
}
}
}
}
# 为3的WebPack
npm install --save-dev extract-text-webpack-plugin
# 为2的WebPack
npm install --save-dev [email protected]
# 为1的WebPack
npm install --save-dev [email protected]
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
]
}
module
plugins
同级
optimization: {
//包清单
runtimeChunk: {
name: "manifest"
},
//拆分公共包
splitChunks: {
cacheGroups: {
//项目公共组件
common: {
chunks: "initial",
name: "common",
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
//第三方组件
vendor: {
test: /node_modules/,
chunks: "initial",
name: "vendor",
priority: 10,
enforce: true
}
}
}
}
npm i -D uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
与webpack.config.js与module
plugins
同级
optimization: {
minimizer: [new UglifyJsPlugin()],
},
模块引用
//在commonjs引入cal对象
// var cal = require('./cal.js');
//es6的模块引入
import cal from './cal.js';
模块导出
// ----------------> cal.js <--------------
//在commonjs导出
// module.exports = cal;
// ES6导出
var cal = 1
export default cal;
// ----------------> cal.js <--------------
var temp1 = '我是默认导出的结果';
export default temp1;
// 导入方式是 import xxx from './cal.js'
//声明式导出
export var obj1 = '我是声明式导出1';
export var obj2 = '我是声明式导出2';
export var obj3 = '我是声明式导出3';
// 导入方式是
// import {obj1,obj2} from './cal.js';
//另一种方式声明导出
var obj4 = '我是声明式导出4'
export {obj4};
// 导入方式是
// import {obj1,obj2,obj4} from './cal.js';
import [,..xxx] [,..from] './xxx.ext'
export default obj;
export var obj = xxx;
export var obj2 = {};
export {stu};
import {obj,obj2,stu} from './xxx.js'; 直接使用obj
import * as newName from '/.cal.js'
npm i [email protected] [email protected] [email protected] [email protected] -D
[email protected]
[email protected]
npm i [email protected] -D
var name = 'abc';
var person = {name}; 简写 相当于--> var person = {name:name};
声明函数
var cal = {
add:function(){
return 1;
},
add2(){
return 2;
},
add3:funtion(n1,n2){
return n1 + n2;
},
add4(n1,n2){ 干掉了function
return n1 + n2;
}
}
// render:function(c){
// return c;
// }
render: c => c(App)
//1:当参数是一个的时候,小括号可以省略
//2:当代码只有一行且是返回值的时候,可以省略大括号及renter
安装
//如果安装报错:invalid "instanceof" keyword value Promise
//需安装以下版本 兼容 [email protected]
npm i [email protected] -D
简单使用 不配置的情况下
// ----------------> package.js <--------------
"scripts": {
"dev": "webpack-dev-server --open --port 3000 --contentBase src" //开发者模式
}
//文件路径方式
"scripts": {
"dev": "..\\node_modules\\.bin\\webpack-dev-server --open --port 3000 --contentBase src" //开发者模式
}
配置
// ----------------> webpack.config.js <--------------
module.exports = {
entry:{ //main是默认入口,也可以是多入口
main:'./src/main.js'
},
//出口
output:{
filename:'./build.js', //指定js文件
path: path.join(__dirname,'dist') //最好是绝对路径
//代表当前目录的上一级的dist
},
devServer:{
contentBase:false,
//我这里没有设置contentBase,个人研究contentBase必须指向存在的bundle.js文件所在目录,
//因为这里是开发模式,所以dist目录并不存在,所以用false.
host:'localhost',
port:'8888',
inline:true,//webpack官方推荐
watchOptions: {
aggregateTimeout: 2000,//浏览器延迟多少秒更新
poll: 1000//每秒检查一次变动
},
compress:true,//一切服务都启用gzip 压缩
historyApiFallback:true,//找不到页面默认跳index.html
hot:true,//启动热更新,必须搭配new webpack.HotModuleReplacementPlugin()插件
open:true,
}
}
使用
// ----------------> package.js <----------------------
"scripts": {
"start": "webpack-dev-server --open" //开发者模式
}
webpack找人来理解你的单文件代码
安装
vue-loader,vue-template-compiler,代码中依赖vue,
npm i [email protected] -S
npm i [email protected] [email protected] -D
<div v-bind:class="isRed?'red':'green'">单个class</div>
<div :class="{'red':true,'big':true}">多个class</div>
// 复杂情况,通过遍历,根据当前对象的成绩,匹配成绩和样式的清单对象,用成绩做key,取对象的value,最终返回字符串做样式名
<ul> //如果stu.score 为 A 就使用red class
<li v-for="stu in stus" :class="{'A':'red','B':'green'}[stu.score]">
{{stu.name}}
</li>
</ul>
v-on:事件名="表达式||函数名"
@事件名="表达式||函数名"
export default {
data() {
return {
Yes:true,
users:[{name:'小红',age:19},{name:'小明',age:18}]
}
},
methods:{
change(){
this.Yes = !this.Yes;
this.users.push({
name:'小红',age:18
})
}
}
}
-
value:{{ use }}
index:{{index}}
vue.component('组件名',组件对象);
//1:引入 vue
import Vue from 'vue';
import App from './app.vue';
//引入子组件对象
import headerVue from './components/header.vue';
import bodyVue from './components/body.vue';
import footerVue from './components/footer.vue';
//声明全局组件
Vue.component('headerVue', headerVue); //注册一个组件,第一个参数是名称,在template中使用,第二个参数是实际的对象,显示成什么内容,具备什么功能
Vue.component('bodyVue', bodyVue);
Vue.component('footerVue', footerVue);
new Vue({
el: '.app',
// render:function(c){
// return c;
// }
render: c => c(App)
//1:当参数是一个的时候,小括号可以省略
//2:当代码只有一行且是返回值的时候,可以省略大括号
})
父传
子接受
头{{ textOne }}{{ testTow }}
src 下新建一个连接器(电话) connector.js
o n 与 on与 on与emit 父接 o n 子 发 on 子发 on子发emit
在connector.js中new 一个 vue
import Vue from 'vue';
var connector = new Vue();
export default connector;
在父子组件中分别引入connector.js
<script>
import connector from './connector.js' // <--------------------
export default {
data() {
return {
text:"呔!妖怪!项目给你搭建好了",
}
},
}
</script>
父组件中打开”电话“ 随时接受子组件的数据 (要想打开才能接受)
<script>
import connector from './connector.js'
export default {
data() {
return {
text:"呔!妖怪!项目给你搭建好了",
}
},
mounted () {
var me = this;
connector.$on('masg',function (msg) { // <-----------------
console.log(msg)
})
}
}
</script>
子组件发送数据 (最好通过元素点击事件来完成发送,想发送的时候再去触发相应事件)
<script>
import connector from './connector.js'
export default {
data() {
return {
text:"呔!妖怪!项目给你搭建好了",
}
},
mounted () {
setTimeout(function() {
connector.$emit('masg', me.msg) // <--------------------
// console.log("yimaio")
}, 500);
}
}
</script>
content | 过滤器,vue中没有提供相关的内置过滤器,可以自定义过滤器
组件内的过滤器 + 全局过滤器
Vue.filter(名,fn)
输入的内容帮我做一个反转
export default {
data() {
return {
title: '哈哈'
}
},
filters:{
filterNOe:function (value) {
//转数组 反转数组 转字符串
return value.split('').reverse().join('')
}
}
}
总结
export default {
data() {
return {
}
},
filters:{
},
components:{
Header
},
mounted () {
console.log(this.$refs.refOne)
}
}
computed示例:
computed:{
payment(){
console.log("改了执行了")
let num = 0; //数量
let sum = 0; //总金额
this.newList.forEach((ele) => {
if (ele.isTrue) {
num += ele.sum
sum += ele.sum * ele.sell_price
}
})
return {
num,sum
}
}
}
//接收
payment.num
payment.sum
//Mint:引入mint-ui
import Mint from 'mint-ui';
//Mint:引入css
import 'mint-ui/lib/style.css';
//Mint:安装插件
Vue.use(Mint);
http://www.dcloud.io/hellomui/
核心:锚点值改变
以后看到vue开头,就知道必须Vue.use
vue的核心插件:
使用方式
1:下载 npm i vue-router -S
2:在main.js中引入 import VueRouter from 'vue-router';
3:安装插件 Vue.use(VueRouter);
4:创建路由对象并配置路由规则
5:将其路由对象传递给Vue的实例,options中
options中加入 router:router
import Home from './components/Home.vue'
let router = new VueRouter({
routes:[
{name:'name'path:'/home',component:Home}
]
});
new Vue({
el: '#app',
render: creater => creater(App),
router
})
6:在app.vue中留坑
去北京
去北京
xxx
{ name:'detail' , path:'/detail',组件}
this.$route.query.id
xxx
{ name:'detail' , path:'/detail/:id',组件}
this.$route.params.id
{name:'xxx',query:{id:1},params:{name:2} }
{ path:'/' ,redirect:'/home' }
{ path:'/' ,redirect:{name:'home'} }
{ path:'*' , component:notFoundVue}
-> name就是default
-> name就是xxx期组件内包含着第一层router-view
{ name:'music' ,path:'/music', component:Music ,
children:[ 子路由的path /就是绝对路径 不/就是相对父级路径
{name:'music.oumei' ,path:'oumei', component:Oumei },
{name:'music.guochan' ,path:'guochan', component:Guochan }
]
}
https://segmentfault.com/a/1190000008470355?utm_source=tuicool&utm_medium=referral
post请求的时候,如果数据是字符串 默认头就是键值对,否则是对象就是application/json
this.$axios.get(url,options)
this.$axios.post(url,data,options)
options:{ params:{id:1}//查询字符串, headers:{ ‘content-type’:‘xxxxx’ },baseURL:’’ }
全局默认设置 :Axios.defaults.baseURL = ‘xxxxx’;
针对当前这一次请求的相关设置
//main.js
import Axios from 'axios';
//给Vue原型挂载一个属性
Vue.prototype.$axios = Axios;
//默认配置
Axios.defaults.baseURL = 'http://182.254.146.100:8899/api/';
//app.vue
export default {
data() {
return {
}
},
created(){
function a(res1,res2){
console.log(res1)
console.log(res2)
}
//合并请求
this.$axios.all([
this.$axios.post('https://api.douban.com/v2/movie/in_theaters',
'name=xiaoming&age=18'
),
this.$axios.get('https://api.douban.com/v2/movie/top250')
])
.then(this.$axios.spread(a))
.catch(err => {
console.log(err);
})
//单get请求
this.$axios.get('http://127.0.0.1:2000/json')
.then(res => {
console.log(res)
console.log(123)
})
.catch(err => {
console.log(err)
})
}
}
关于axios post请求 参数错误问题
var params = new URLSearchParams();
params.append('data',JSON.stringify(data));
this.$axios.post('upData',params,
{header:{'Content-Type': 'application/x-www-form-urlencoded'}})
.then(res => {
})
.catch(err => {
console.log(err);
})
import { Indicator } from 'mint-ui';
//在请求之前
Axios.interceptors.request.use(function (config) {
Indicator.open({
text: '加载中...',
spinnerType: 'fading-circle'
});
return config //必须return 出去
})
//在请求结束后
Axios.interceptors.response.use(function (config) {
Indicator.close();
return config //必须return 出去
})
单个数据监听
export default{
data() {
return {
text3:0
}
},
watch:{
text3:function () {
// text3值发生改变的时候触发
console.log('改变了')
}
},
methods:{
change(){
this.text3+=1
}
}
}
*
=
{{ sum }}
moment
安装:
bower install moment --S
使用:
//引用
import moment from 'moment'
//传入值
var value = '2019-04-24T16:30:08.417'
moment(value).format('YYYY-MM-DD') //2019-04-24
js 实现时间格式化
var time = 1000; //单位秒
var formatTime = function (time) {
/*00:00:00*/
var h = Math.floor(time/3600);
var m = Math.floor(time%3600/60);
var s = Math.floor(time%60);
return (h<10?'0'+h:h)+':'+(m<10?'0'+m:m)+':'+(s<10?'0'+s:s)
}
https://github.com/LS1231/vue-preview
{
test: /\.css$/,
include: [
/src/,//表示在src目录下的css需要编译
'/node_modules/bootstrap/' //增加此项
],
loader: 'style-loader!css-loader!autoprefixer-loader'
}
https://mint-ui.github.io/docs/#/zh-cn/loadmore
- {{ item }}
//父盒子main-body css必须加 overflow-y: scroll; !!!!!!!!!!!!!!!!
export default {
data() {
return {
allLoaded:false, //是否禁止触发上拉函数 取消上拉就改为true
isAutoFill:false,//是否自动触发上拉函数
lists: [],
wrapperHeight:0
}
},
mounted() {
// 父控件要加上高度,否则会出现上拉不动的情况
this.wrapperHeight =
document.documentElement.clientHeight -
this.$refs.wrapper.getBoundingClientRect().top;
},
methods: {
loadBottom() {
console.log("上拉触发啦");
//通知上拉操作已经完结
// console.log(this.$refs.loadmore.onBottomLoaded());
this.$refs.loadmore.onBottomLoaded();
}
},
}
this.lists = this.lists.concat(response.data.data)
//concat 追加
安装:
npm i formidable -S
*引用
const formidable = require('formidable')
同步提交基本使用
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="file" id="file">
<input type="submit" name="" value="提交">
form>
app.post('/upload',function (request,responst, ) {
var form = new formidable.IncomingForm();
//为了能更好的保存文件在本地服务器目录 保存路径
form.uploadDir = path.join(__dirname,'files');
//解析请求
form.parse(request, function(err, fields, files) {
if(err) next(err);
console.log('上传文件完毕')
});
responst.send("hello")
})
异步提交:
<form>
<input type="file" name="" id="file">
<input type="submit" name="" value="提交">
form>
document.querySelector('form').onsubmit = function(e){
e.preventDefault(); //取消默认事件
//错误代码的出现会导致return false不执行,不太好调试程序
var formData = new FormData();
//第一个参数key和表单中的name的值是一个意思
//第二个参数是文件的数据对象
formData.append('myFile',document.getElementById('file').files[0])
var xhr = new XMLHttpRequest();
xhr.open('post','/upload');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
alert('成功');
}
}
xhr.send(formData);
// return false;
}
jquery 使用 formidable
$('form').on('submit',function(e){
var formData = new FormData();
formData.append('myFile',document.getElementById('file').files[0]);
e.preventDefault();
//jq默认头是content-type: www-url..... 键值对
$.ajax({ // Illegal invocation
url:'/upload',
type:'post',
data:formData, //当传递formData的时候,就会默认将该对象转换成键值对字符串,不合理的
//jq上传文件必须要以下两个属性
contentType:false,//不需要头
processData:false,//不转换数据
success:function(){
console.log('ok')
}
})
})
created() {
// Event.$on('sum_subtract',function (sum_subtract) {
// console.log(sum_subtract)
//这里的 this.sum_subtract 指向的不是Vue data 中的 sum_subtract
// this.sum_subtract += sum_subtract
// console.log(this.sum_subtract)
// })
//需要通过箭头函数来使用
//箭头函数当前找不到匹配的this就会去上一级找相匹配的this
Event.$on('sum_subtract',sum_subtract => {
this.sum_subtract = this.sum_subtract + sum_subtract
console.log(this.sum_subtract)
})
}
可以在属性中声明 JavaScript 钩子
.show-eneter-active,
.show-leave-active{
/* 过渡开始到过渡结束 */
transition: opacity .5s;
}
.show-enter,.show-leave-to{
/* 过渡开始状态与过渡结束后状态 */
opacity: 0;
}
同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了 过渡模式
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。out-in
:当前元素先进行过渡,完成之后新元素过渡进入。用 out-in
重写之前的开关按钮过渡:
window.localStorage
let prodTools = {};
let store = window.localStorage;
// let prods = {没有数据就应该是空对象}; //未来从localStorage中获取
let prods = JSON.parse(store.getItem('prods')||'{}');
//{ id:num }
//增加或追加
prodTools.addOrUpdate = function(p){
//判断是否存在
if(prods[p.id]){//追加
prods[p.id] += p.num;
}else{
prods[p.id] = p.num;
}
//保存
this.saveProds(prods);
}
//删除
prodTools.delete = function(id){
//es5 相当于 prods.id
delete prods[id];
//保存
this.saveProds(prods);
}
//获取到storage
prodTools.getProds = function(){
return JSON.parse(store.getItem('prods')||'{}');
}
//获取总数
prodTools.getTotalCount = function(){
let sum = 0;
for(let id in prods){
sum += prods[id];
}
return sum;
}
//存储
prodTools.saveProds = function(prods){
//第一个参数是key 第二个是 value
store.setItem('prods',JSON.stringify(prods));
}
export default prodTools;
beforeRouteEnter (to, from, next) {
在请求之前
在渲染该组件的对应路由被 confirm 前调用
不!能!获取组件实例 `this`
因为当钩子执行前,组件实例还没被创建
},
beforeRouteEnter (to, from, next) {
console.log(from.name)
let titlee =''
if (from.name == "goodsDetail") {
titlee = "图文详情"
}else if (from.name == "newsList"){
titlee = "新闻详情"
}
console.log(4545)
next(vm => {
vm.titlee = titlee
})
}
beforeRouteUpdate (to, from, next) {
在当前路由改变,但是该组件被复用时调用
举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
导航离开该组件的对应路由时调用
可以访问组件实例 `this`
}
//在vue中添加属性,是没有办法做到响应,setter
// ele.num = 123;
// ele.isPicked = true;
//添加属性一定要this.$set
//三个参数 对象 属性名 值
this.$set(ele,'num',123);
this.$set(ele,'isPicked',true);
1.将 config/index.js 中的build:{}中的 assetsPublicPath: ‘/’,改为 assetsPublicPath: ‘./’,
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './', //改这里!!!!!
//存储并转成json字符串
sessionStorage.setItem('user',JSON.stringify(this.responseText));
//获取
sessionStorage.getItem('user');
//指定删除
sessionStorage.removeItem('user');
//清空所有
sessionStorage.clear();
2.执行命令:
npm run build
3.安装http-server 后 运行http-server 可本地服务器访问
*常用技巧
1.打包后ttf字体图标路径问题
更改 build/utils.js 文件中 ExtractTextPlugin 插件的options 配置:
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
publicPath: '../../', // 注意配置这一部分,根据目录结构自由调整
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
2.如果在config -> index.js 中的 build 代码中的 productionSourceMap的值设为false ,打包后文件体积可以减少百分之八十
3.如果设置build文件夹下的webpack.prod.conf.js中HtmlWebpackPlugin插件配置参数添加hash: true,即会使打包生成的index.html中的js和css路径带有?+随机字符串,也就是版本控制
const Foo = resolve => require(['./Foo.vue'],resolve) //列子
import Home from '@/components/home' //默认
//懒加载
const Home = resolve =>require(['@/components/home'],resolve)
height: 100%;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
module.exports = {
lintOnSave:false
}
点击File 下的 settings 如图 (如果以新建了上面 39 方法的vue.config.js 的话 是找不到 ESLint 这一选项的 须删除vue.config.js 文件 或 注销其内容)
错误显示为重复的key值
解决方法:不一定要拿循环出的index值来当key的值可以用循环的对象id或其它属性来代替index
window.addEventListener(‘hashchange’, function() {
var text = ‘’;
switch (location.hash) {
case ‘#/music’:
text = ‘各种音乐的数据’;
break;
case ‘#/movie’:
text = ‘各种电影的数据’;
break;
}
来了 api 要用趁早哦
用到的api
- get 请求获取所有品牌列表的 api
http://kerys.pythonanywhere.com/api/getprodlist/
调用方式: GET 请求直接调用
- post 请求添加品牌的 api
http://kerys.pythonanywhere.com/api/addprod/
调用方式:POST 请求 需要的参数 {name: '品牌名称'} , {emulateJSON:true} 普通表单请求
- get 请求删除品牌的 api
http://kerys.pythonanywhere.com/api/delprod/id
调用方式: GET 请求 id 是品牌的id int类型