目錄
一、Promise
async函數會返回一個promise,並且Promise對象的狀態值是resolved(成功的)
//Promise基礎用法
let pro = new Promise(resolve => {
setTimeout(() => {
resolve('hello world')
}, 500)
})
pro.then(res => {
console.log(res) // hello world
})
//Promise和async await相結合,async函數會返回一個promise,並且Promise對象的狀態值是resolved(成功的)
const fun = async () => {
let result
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
code: 200,
data: { name: 'zhangsan' },
message: '姓名'
})
}, 1000)
}).then(res => {
result = res
})
return result
}
////Promise和async await相結合二
fun().then(res => {
console.log(res)
})
const fun2 = async () => {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
code: 200,
data: 'lishi',
message: '消息'
})
}, 1500)
})
}
fun2().then(res => {
console.log(res)
})
二、swiper
swiper.js:
<template>
<div>
<div>
<swiper v-if="bannerList.length > 0" :options="options">
<swiper-slide v-for="(item,index) in bannerList" :key="index">
<img :src="item" class="m-banner-img"/>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
<div class="swiper-button-prev" slot="button-prev"></div>
<div class="swiper-button-next" slot="button-next"></div>
</swiper>
</div>
</div>
</template>
<script>
import Api from "../api";
export default {
data() {
return {
bannerList: [],
options: {
loop: true,
effect: 'cube', //slide, fade,cube, coverflow, flip
speed: 1000,
autoplay: {
delay: 2000
},
pagination: {
el: '.swiper-pagination',
clickable: true
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
}
};
},
mounted() {
Api.getBanner().then(res => {
if (res.code === 200) {
this.bannerList = res.data;
}
});
}
};
</script>
<style>
</style>
main.js:
import Vue from 'vue'
import lazyload from 'vue-lazyload'
import App from './App.vue'
import router from './router'
import store from './store'
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'
import './font/iconfont.css'
import './index.css'
import img from './images/loading.png'
Vue.config.productionTip = false
Vue.use(lazyload, {
loading: img
})
Vue.use(VueAwesomeSwiper)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
三、$event
有時也需要在內聯語句處理器中訪問原始的 DOM 事件。可以用特殊變量 $event
把它傳入方法。
四、transition 過渡動畫
<template>
<div>
<div>
<router-link to="/index/home" class="m-nav-item">首頁</router-link>
<router-link to="/index/my_book" class="m-nav-item">書包</router-link>
<router-link to="/index/exam1" class="m-nav-item">週考1</router-link>
</div>
<transition name="slide">
<router-view class="m-router"></router-view>
</transition>
</div>
</template>
<script>
export default {
}
</script>
<style>
.m-router{position: absolute;width:100%}
.slide-enter-active{transition: all 1s linear;}
.slide-enter{transform: translateX(100%)}
/* .slide-leave-active{transition: all 1s linear;}
.slide-leave-to{transform: translateX(-100%)} */
.slide-leave-active{animation: slide 1s linear;}
@keyframes slide {
0% {transform: translateX(0)}
100% { transform: translateX(-100%)}
}
</style>
五、toast組件
Toast.vue:
<template>
<div class="m-toast-mask" :id="id" >
<div class="m-toast">{{message}}</div>
</div>
</template>
<script>
export default {
data() {
return {
duration: 3000,
message: '',
id: Date.now()
}
},
mounted() {
setTimeout(() => {
document.getElementById(this.id).remove()
}, this.duration)
}
}
</script>
<style>
</style>
index.js:
import Vue from 'vue'
import Toast from './Toast'
const ToastConstructor = Vue.extend(Toast)
const toast = (options) => {
let instance = new ToastConstructor({data: options}).$mount()
document.body.appendChild(instance.$el)
}
export default toast
css:
.m-toast-mask{display: flex; position: fixed;top: 0;left: 0;right: 0;bottom: 0;}
.m-toast{margin: auto;padding: 0 10px; min-width: 100px;line-height: 40px;border-radius: 3px;background: rgba(0, 0, 0, 0.5);text-align: center;color: #ffffff;}
使用:
<template>
<div>
<div v-for="(item, index) in myBook" :key="item.id" class="m-my-book-item">
<div class="m-my-book-info">
<label>
<input type="checkbox" :checked="item.checked" @change="handleCheck(index, $event)" />
{{item.title}}
</label>
¥{{item.price}}
</div>
<div class="m-my-book-action">
<button @click="handleSub(index)">-</button>
{{item.count}}
<button @click="handleAdd(index)">+</button>
<button @click="handleDelete(index)">刪除</button>
</div>
</div>
<div v-if="myBook.length > 0">
<div>
<label>
<input type="checkbox" :checked="total.checkedAll" @click="handleCheckAll">
全選
</label>
<button @click="handleShowDialog">刪除</button>
</div>
<div>總價:¥{{total.totalPrice}},總數:{{total.totalCount}}</div>
</div>
<div v-else>書包空空如也~~~</div>
<Dialog :visible="visible" title="刪除">
<template v-slot:content>
<div class="m-delete-info">
<Icon type="shanchu" classname="m-delete-icon"></Icon>
<div class="m-delete-text">
你確定要刪除選中的商品嗎?
</div>
</div>
</template>
<template v-slot:footer>
<button class="m-btn" @click="handleHideDialog">取消</button>
<button class="m-btn" @click="handleDeleteChecked">確定</button>
</template>
</Dialog>
</div>
</template>
<script>
import Api from "../api";
import Dialog from '../components/Dialog'
import Icon from '../components/Icon'
import toast from '../components/Toast'
export default {
data() {
return {
visible: false
}
},
computed: {
myBook() {
return this.$store.state.myBook;
},
total() {
let myBook = this.myBook;
let totalCount = myBook.filter(item => item.checked).reduce((total, item) => {
return total + item.count;
}, 0);
let totalPrice = myBook.filter(item => item.checked).reduce((total, item) => {
return total + item.count * item.price;
}, 0);
return {
totalCount,
totalPrice,
checkedAll: myBook.every(item => item.checked)
};
}
},
components:{
Dialog,
Icon
},
methods: {
handleSub(index) {
let myBook = this.myBook;
if (myBook[index].count > 1) {
myBook[index].count--;
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
}
},
handleAdd(index) {
let myBook = this.myBook;
myBook[index].count++;
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
},
handleDelete(index) {
let myBook = this.myBook;
myBook.splice(index, 1)
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
},
handleCheck(index, e) {
let myBook = this.myBook;
myBook[index].checked = e.target.checked
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
},
handleCheckAll(e) {
let myBook = this.myBook;
myBook.forEach(item => {
item.checked = e.target.checked
})
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
},
handleDeleteChecked() {
let myBook = this.myBook;
myBook = myBook.filter(item => !item.checked)
this.$store.commit({ type: "setState", key: "myBook", value: myBook });
this.handleHideDialog()
},
handleShowDialog() {
if (this.myBook.filter(item => item.checked).length === 0) {
//alert('請選擇要刪除的商品~')
toast({message: '請選擇要刪除的商品~', duration: 1000})
return
}
this.visible = true
},
handleHideDialog() {
this.visible = false
}
},
updated() {
Api.update({ myBookNew: this.myBook }).then(res => {});
},
mounted() {
this.$store.dispatch({ type: "getMyBook" });
}
};
</script>
<style>
</style>
註冊到全局:
import toast from './components/Toast'
Vue.prototype.$toast = toast
使用:
this.$toast({message: 'test'})
appendChild() 方法在指定元素節點的最後一個子節點之後添加節點。
https://www.w3school.com.cn/xmldom/met_element_appendchild.asp
六、解構並不能實現對象的深拷貝
let obj = {
a: 1,
b: {
c: '1'
},
fun() {
console.log(1)
},
time: new Date(),
d: undefined
}
let newObj = { ...obj }
newObj.b.c = '2'
console.log(obj)
console.log(newObj)
console.log(obj.b.c) // 2
七、修改腳手架默認包管理工具
八、redux
reducer 一定要保持純淨,只要傳入參數相同,返回計算得到的下一個 state 就一定相同。沒有特殊情況、沒有副作用,沒有 API 請求、沒有變量修改,單純執行計算。
時刻謹記永遠不要在克隆 state
前修改它。
在redux-devtools中,我們可以查看到redux下所有通過reducer更新state的記錄,每一個記錄都對應着內存中某一個具體的state,讓用戶可以追溯到每一次歷史操作產生與執行時,當時的具體狀態,這也是使用redux管理狀態的重要優勢之一.
若不創建副本,redux的所有操作都將指向內存中的同一個state,我們將無從獲取每一次操作前後,state的具體狀態與改變,若沒有副本,redux-devtools列表裏所有的state都將被最後一次操作的結果所取代.我們將無法追溯state變更的歷史記錄.
創建副本也是爲了保證向下傳入的this.props與nextProps能得到正確的值,以便我們能夠利用前後props的改變情況以決定如何render組件。
直接修改state(錯誤的):
const defaultState = {
count: 0
}
//直接修改改state,所有的state都將被最後一次操作的結果所取代.我們將無法追溯state變更的歷史記錄
const reducer = (state = defaultState ) => {
state.count++
return state
}
let previousState = { count: 0 }
let newState = reducer(previousState)
console.log(previousState) //{ count: 1 }
console.log(newState) //{ count: 1 }
深拷貝(性能很差):
const defaultState = {
count: 0
}
//深拷貝可以解決這個問題
const reducer = (state = defaultState ) => {
state = JSON.parse(JSON.stringify(state))
state.count++
return state
}
let previousState = { count: 0 }
let newState = reducer(previousState)
console.log(previousState) //{ count: 0 }
console.log(newState) //{ count: 1 }
解構:
const defaultState = {
count: 0
}
//解構可以解決這個問題
const reducer = (state = defaultState ) => {
let newState = {...state}
newState.count++
return newState
}
let previousState = { count: 0 }
let newState = reducer(previousState)
console.log(previousState) //{ count: 0 }
console.log(newState) //{ count: 1 }
解構屬於淺拷貝,解決不了嵌套的場景:
const defaultState = {
obj: {
count: 0
}
}
//解構屬於淺拷貝,解決不了嵌套問題
const reducer = (state = defaultState ) => {
let newState = {...state}
newState.obj.count++
return newState
}
let previousState = {
obj: {
count: 0
}
}
let newState = reducer(previousState)
console.log(previousState) //{ obj: { count: 1 } }
console.log(newState) //{ obj: { count: 1 } }
非要用解構,也行:
const defaultState = {
obj: {
count: 0
}
}
//解構屬於淺拷貝,解決不了嵌套問題,不過可以這樣解決,只是比較難理解
const reducer = (state = defaultState ) => {
let newObj = {...state.obj}
newObj.count++
return {...state, obj: newObj}
}
let previousState = {
obj: {
count: 0
}
}
let newState = reducer(previousState)
console.log(previousState) //{ obj: { count: 0 } }
console.log(newState) //{ obj: { count: 1 } }
immutable.js:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
<script>
const defaultState = Immutable.fromJS({
obj: {
count: 0
}
})
//推薦使用immutable.js,由Facebook工程師耗時三年完成!
const reducer = (state = defaultState ) => {
return state.setIn(['obj', 'count'], Immutable.fromJS(state.getIn(['obj', 'count']) + 1))
}
let previousState = Immutable.fromJS({
obj: {
count: 0
}
})
let newState = reducer(previousState)
console.log(previousState.getIn(['obj']).toJS()) //{ count: 0 }
console.log(newState.getIn(['obj']).toJS()) //{ count: 1 }
</script>
</div>
</body>
</html>
把業務代碼提出來:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<script src="https://cdn.bootcss.com/immutable/4.0.0-rc.12/immutable.js"></script>
<script>
const defaultState = Immutable.fromJS({
obj: {
count: 0
}
})
//推薦使用immutable.js,有Facebook工程師耗時三年完成!可以把業務代碼提出來,reducer變成動態的key和動態的value
const reducer = (state = defaultState, action ) => {
return state.setIn(action.key, Immutable.fromJS(action.value))
}
let previousState = Immutable.fromJS({
obj: {
count: 0
}
})
let action = { key: ['obj'], value: { count : 1} }
let newState = reducer(previousState, action)
console.log(previousState.getIn(['obj']).toJS()) //{ count: 0 }
console.log(newState.getIn(['obj']).toJS()) //{ count: 1 }
</script>
</div>
</body>
</html>
九、react組件props類型檢查和默認值
不需要安裝prop-types包,腳手架已經默認安裝
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Icon extends Component {
// static defaultProps = {
// type: 'xingxing'
// }
render() {
let { type, className } = this.props
return (
<span className={`icon iconfont icon-${type} ${className ? className : ''}`} onClick={this.props.onClick}></span>
)
}
}
Icon.propTypes = {
type: PropTypes.string,
className: PropTypes.string
}
Icon.defaultProps = {
type: 'shubao'
}
參考鏈接:
https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#___gatsby
https://www.npmjs.com/package/prop-types
十、使用webpack搭建react開發環境
webpack.config.js:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmeWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: __dirname + '/index.js',
output: {
path: __dirname + '/dist',
filename: 'bundle-[hash].js'
},
devServer: {
inline: true,
hot: true,
port: 9000,
contentBase: __dirname + '/dist',
open: true
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-react", "@babel/preset-env"]
}
},
exclude: /node_modules/
}, {
test: /\.css$/,
use: [{
loader: MiniCssExtractPlugin.loader
}, {
loader: 'css-loader'
}]
}]
},
plugins: [
new HtmeWebpackPlugin({
template: __dirname + '/public/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name]-[hash].css'
})
]
}
package.json:
{
"name": "toast",
"version": "1.0.0",
"description": "",
"main": "/toast/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.4",
"babel-loader": "^8.0.6",
"css-loader": "^3.4.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"style-loader": "^1.1.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
}
}
github:
十一、webpack搭建react項目時在二級路由頁面刷新報404的解決辦法
react-router 使用webpack-dev-server做服務器時 單級刷新報錯 Cannot GET /xx。多級刷新報錯404
output: {
path: __dirname + '/dist',
filename: 'bundle-[hash].js',
publicPath: '/' //第二處要添加的
},
devServer: {
inline: true,
hot: true,
port: 9000,
contentBase: __dirname + '/dist',
open: true,
historyApiFallback: true //第一處要添加的
},
參考鏈接:
https://blog.csdn.net/limonsea/article/details/96865906
十二、webpack圖解
webpack+webpack-dev-server:
https://segmentfault.com/q/1010000005767033/a-1020000005767079
通過node啓動webpack-dev-server:
https://webpack.docschina.org/guides/hot-module-replacement/#%E9%80%9A%E8%BF%87-node-js-api
devServer.overlay:
這個配置屬性用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤,默認是false,可設置爲true
devServer: {
overlay: true
}
十三、import對象解構失敗問題
用慣了es6語法中的解構賦值,在對於import引入的對象進行解構賦值時發現對象變成了undefined
。
解決辦法:
// main.js
import { foo, bar } from "./static"
// static.js
let foo = "foo"
let bar = "bar"
export { foo, bar }
參考鏈接:
https://blog.csdn.net/momDIY/article/details/88375677
十四、FileZilla連接ftp服務器失敗,提示"AUTH TLS"解決方法
https://www.cnblogs.com/mytt/p/6684199.html
github源碼:
https://github.com/baweireact/m-apps/tree/master/m-app-1708E