簡單電商項目代碼地址:https://github.com/Betty09Zhang/shoppingMall
效果瀏覽:www.bettyzm.clude:4000/index.html 登錄名:zm 密碼:123
1. slot 插槽(父子組件通信)
全局模態框組件實現:
在modal.vue 中添加槽
<slot name="tip"></slot>
父傳子
在父組件中直接添加組件
<modal v-bind:class="{'md-show':isShow}" v-on:close="closeModal">
<p slot="tips">請先登錄!</p>
<div slot="closeDialog" @click="isShow=false">關閉</div>
</modal>
在子組件中引入isShow 變量 props["isShow"]
子傳父
通過觸發在父組件定義的事件 如close =====> 執行父組件中定義的closeModal 方法。
methods:{
closeModal(){
this.isShow=false;
},
在子組件中,通過this.$emit('close') 觸發
emit/on 事件發佈 和訂閱
2.測試技巧
模擬mock 數據,json 文件驗證,不用後臺數據。
3. 主頁面開發
頭部:mint-ui headbar
底部:m-ui tarbar www.dcloud.io/hellomui (可更換ICON 圖標)
移動端 雙飛翼 搜索框佈局
input 搜索框 display:inline-block 轉換爲行內塊才能一行顯示。
3.1 商品列表開發
加載商品圖片時, <a href="#"><img v-bind:src=" ‘/static/1.img‘’" alt=""></a> 圖片需要動態賦值,否則渲染DOM樹是,圖像來不及加載。動態加載時,一開始無SRC屬性,就不會造成圖像路徑找不到的錯誤,使用命令後,需要用單引號將其引入,因爲使用命令後,就要使用js 語句,不再是字符串,要使用字符串時必須用單引號引入。
客戶端發出請求,可用axios插件。
axios
基於promise用於瀏覽器和node.js的http客戶端
特點
- 支持瀏覽器和node.js
- 支持promise
- 能攔截請求和響應
- 能轉換請求和響應數據
- 能取消請求
- 自動轉換JSON數據
- 瀏覽器端支持防止CSRF(跨站請求僞造)
axois npm 安裝後,導入模塊以後,需要將其掛載爲vue 的原型對象才能使用
Vue.prototype.$axios=axios
若爲跨域請求,需要在服務器端的入口函數中 設置服務器的response Header
//設置響應頭response header
var app=express()
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
next();
});
在 利用axios跨域接收 服務器響應的cookie 時,這是不能設置響應頭 res.header("Access-Control-Allow-Origin", "*");必須指定具體前端頁面的域名和端口。在響應頭裏面加上MIME TYPE 的類型。
eg: res.header("Access-Control-Allow-Origin", "http://127.0.0.1:8080");
res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("Content-Type", "application/json;charset=utf-8");
開發時,webpack -dev -server 默認是以本地地址 http://localhost/8080 打開前端頁面,要指定具體的域來打開,不要用localhost,否則 服務器響應的cookie 不會由axios跨域訪問寫到客戶端
設置cookie 利用express封裝的方法 response.cookie("userId",value); 獲取cookie request.headers.cookie。
在使用axio 請求時,在then 的內部不能使用vue 的實例化this ,因爲在內部沒有被綁定。
解決方案:用es6 箭頭函數,箭頭函數可以和父方法共享變量。
export default {
data(){
return {
newsList:[]
}
},
created(){
this.$ajax.get('/news/').then(function(response){
this.newsList=response.data.list;=======>// 注意response.data 纔可以接收到響應數據。
console.log(this.newsList);
}).catch(function(error){
console.log(error)
})
}
}
axios get傳參請求,添加params 字段,傳遞參數;
var param={pageSize:this.pageSize,pageIndex:this.pageIndex,sort:this.sortFlag?1:-1};
this.$ajax.get('/goods',{'params':param}).then((response)=>{...
Vue實例中有$route 和$router 屬性,可通過控制檯輸出this 查看。
在Vue2.0 this.$route.query ,this.$route.params 接收router-link 傳的參數。
$route 爲當前router 跳轉對象裏面可以獲取得name,path,query,params 等。
$router 爲 VueRouter 實例,想要導航到不同url,可使用$router.push(‘要跳轉的路徑名’) 的方法。
服務端問題:
服務端express 路由get 方法傳參,url中 需用佔位符
var router=express.Router();
router.get('/newsDetails/:id',function(req,resp,next){
// resp.send('獲取服務端信息 goods');
// 接收到請求後,利用模型取數據庫裏面查找 有findAPI
var id=req.params.id;
console.log("id:"+id);....
}
vue-inifinite-scroll 分頁插件 鼠標滾動之後調用的方法 鼠標滾輪距離底部多遠時開始加載滾動接口
<div v-infinite-scroll="loadMore" infinite-scroll-disabled="busy" infinite-scroll-distance="10"></div>
注意:初始值 應設置busy 爲 false。busy=true 表示禁止加載顯示。
4.數據格式問題
get 請求
url 傳值 通過request 獲取
http://localhost:4000/goods?pageSize=3&pageIndex=2&sort=1 ==》express 框架取參數: request.query("pageSize");
http://localhost:4000/goods/3 ==》express 框架取參數: request.params("pageSize");
原生nodejs 是通過 request.url
router.get('/',function(req,resp,next){
// resp.send('獲取服務端信息 goods');
// 接收到請求後,利用模型取數據庫裏面查找 有findAPI
var pageIndex=req.query('pageIndex');
console.log(pageIndex);
var pageSize=parseInt(req.param('pageSize'));
var param={};
var nums=pageSize*(pageIndex-1);
var sort=req.param('sort');
var goodsModel=Goods.find(param).skip(nums).limit(pageSize);
goodsModel.sort({'salePrice':sort});
goodsModel.exec(function(err,doc){
if(err){
resp.json({
status: '0',
msg: err
})
}
else{
resp.json({
status:'1',
msg: '',
count: doc.length,
list:doc
})
}
})
})
post 請求
通過request.body.屬性 來獲取參數。
注意:在瀏覽器端定義完數據模型model 後記得導出 module.exports=mongoose.model('Users',userSchema);
日期時間轉換 : 定義爲Date 日期型格式數據從數據庫取出數據時格式爲 2015-04-23T00:00:00.000Z,可用moment 日期轉換時間插件來轉化。在main.js 中定義一個過濾器即可。
response.json({}) // 輸出json 格式數據
response.end() // 輸出字符串格式數據
router.get('/',function(req,res,next){
})
var app=express()
app.use(router) =========> 設置完路由之後 ,記得掛載路由。
5.img 動態加載路徑問題
跑起來發現圖片不顯示,錯誤碼爲404,
原因:在webpack中會將圖片圖片來當做模塊來用,因爲是動態加載的,所以url-loader將無法解析圖片地址,然後npm run dev 或者npm run build之後導致路徑沒有被加工【被webpack解析到的路徑都會被解析爲/static/img/[filename].png,完整地址爲localhost:8080/static/img/[filename].png】
利用static目錄 ,而不是把圖片放到別的地方,比如你自己新建一個img文件夾,在動態引用是無效的
js動態生成的路徑無法被url-loader解析到,如果你去build,會發現圖片甚至不會打包輸出到dist目錄(webpack是按需打包的)。
解決辦法:①將圖片作爲模塊加載進去,比如images:[{src:require(‘./1.png’)},{src:require(‘./2.png’)}]這樣webpack就能將其解析。②將圖片放到static目錄下,但必須寫成絕對路徑如images:[{src:”/static/1.png”},{src:”/static/2.png”}]這樣圖片也會顯示出來,任何放在static/中文件需要以絕對路徑的形式引用:/static[filename]
<a href="#"><img :src=" '/static/'+item.productImage" alt=""></a>
5.router-link 點擊設置
設置 鏈接激活時使用的 CSS 類名。默認值可以通過路由的構造選項 linkActiveClass
來全局配置。
6.引入自己的樣式路徑問題
./
: 當前目錄下; ./component 表示當前目錄同級component文件目錄
../
: 父級目錄;
定義一個css 文件,在main.js 中引入(import 即可)
7.登錄攔截
採用express 中間件
app.use(function(req,resp,next){
console.log("req.originalUrl: "+req.originalUrl);
console.log("req.path: "+req.path);
console.log("cookie:"+req.headers.cookie);
if(req.headers.cookie){
console.log('已登錄');
next();
}
else{
//白名單
if(req.originalUrl=='/users/logout' || req.originalUrl=='/users/login' || req.originalUrl=='/news/newsList' || (req.originalUrl.indexOf('/goods/list')>-1)){
console.log('白名單 通過!');
next();
}
else{
console.log('請先登錄!');
resp.json({ ====> 傳數據到前端===> 前端根據狀態碼 彈框提示
status:'0',
msg:'請登錄',
result:''
});
}
}
})
enter 登錄
在密碼輸入框 中註冊 回車鍵的keyup 時間 @keyup.enter
update 更新mongoose
Users.update({'userId':userId,'cartList.productId':productId},{'cartList.$.productNum':productNum,'cartList.$.checked':checked},function(err,doc){
if(err){
resp.json({
status: '0',
msg: err.message
})
}
else{
resp.json({
status:'1',
msg:'',
result:'success'
});
}
})
8 利用computed 屬性實時監控變量,得到另一變量的值
1.監控商品選中框狀態,是否選中全選按鈕。
2. 監控商品數量按鈕,實時改變價格。
在computed 屬性中定義變量: 例子:
|
computed:{ allSelectedFlag(){ for(var i=0;i<this.cartList.length;i++){ countSum(){ |
8. 定義過濾器 對時間、數字等進行過濾處理,保留兩位小數等。
8.1 局部過濾器。
import 導入
在 export default{} 中 定義過濾器 filters: {
過濾器名稱:導入模塊名
}
import {currency} from '.././util/currency.js'
export default{
data(){
return{
cartList:[],
isShow:false,
productId:'',
}
},
mounted(){
this.getCart();
},
filters:{
currency: currency
},
8.2 全局過濾器
import 導入 Vue.filter(過濾器名稱,導入模塊名);
| 過濾器名稱 使用
<div class="item-total">
Item total: <span class="total-price">{{countSum | currency('$')}}</span>
</div>
import './util/currency.js'
//掛載
Vue.prototype.$ajax=Axios;
Axios.defaults.baseURL='http://127.0.0.1:4000';
Axios.defaults.withCredentials=true;
Vue.filter('converDate',function(value) {
return Moment(value).format('YYYY-MM-DD');
});
Vue.filter('currency',currency);
9.mongoose update $pull刪除字段對應數組中的對象
10.跳轉頁面
this.$router.push({path: '/user/item?addressId='+ temp})
11. vuex 統一狀態管理
在項目中如購物車的數量等需要實時的在各個單頁面統一更新,若利用父子組件通信傳值,則較不方便,若有一個全局的變量來進行更新,則方便許多,在mutations 中通過commit 方法修改變量值,需要用到的數據封裝成一個對象payload,一起傳過去。
npm install vuex --save
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
在vue 中註冊。
var store=new Vuex.Store({
state:{
count:0
},
mutations:{
getCount(state,payload){
if(payload.flag=='add'){
console.log('數量count:'+store.state.count);
console.log('數量傳參:'+payload.count);
store.state.count+=payload.count;
console.log('數量結果:'+store.state.count);
}
else if(payload.flag=='minus'){
store.state.count-=payload.count;
}
}
}
})
new Vue({
el:'#myapp',
store,
router: router,
render: function(create){
return create(App);
}
})
在組件footer.vue 中
methods:{
getCart(){
this.$ajax.get('/users/cart').then((response)=>{
if(response){
var re=response.data;
if(re.status=='1'){
this.cartList=re.result.cartList;
console.log('購物車數量:'+re.cartCount);
var payload={
flag:'add',
count: re.cartCount
}
this.$store.commit("getCount",payload);
}
}
else{
console.log('獲取購物車失敗!');
}
})
}
12. 樣式上需要注意的點:
在 style 中引入css ,scss 文件 @import '../../style/mixin' ; 分號分割;
定義 animation動畫 @keyframes tipMove{
0%{ transform: scale(1)}
}