一、webpack配置
背一下webpack到底是个什么东西?
接下来,由于这个项目的源码用的webpack3,而现在都是webpack4了,所以自己在webpack中花费了很多的时间,记录一下其中的问题:今天下午解决了一个很困扰的问题,大致是这样的,我创建了一个css文件,打包后css用引入的图片老不对,所以图片无法在网页中展示,查阅大量资料发现
output:{
filename:'js/[name].js',
path:path.resolve(__dirname,'dist'),
publicPath : '../'
},
这里所有打包的文件都放在我们设置的dist目录下,例如filename:'js/[name].js'就是放在dist下边的js目录中
而publicPath的作用是,我们对静态资源的访问可以借助这个,但是不会影响文件打包的路径,比如我没加publicPath之前我的css文件引入图片路径是这样的
我加了publicPath就正常了,图片可以显示了
因为对静态资源的引入有一个公式 ,可参考这篇文章
静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径
这个问题终于搞明白了,新的问题又来了,就是关于在webpack-dev-server配置时,我按网上的教程分别写了output.publicPath和devserver.publicPath ,但是在我打包时他按照我上边的方式配置,图片是能正常显示的,但是devserver不能自己刷新了,不起效果了,查阅了大量资料无果后我配置了两个webpack.config.js这样就分别能在两个环境下都正常运行了,但是我这个问题还是没有解决。
1.所有用到的插件
1)js文件会自动打包不用配置
2)关于css文件的打包
style-loader安装
css-loader安装
MiniCssExtractPlugin 安装并引入
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
//css-loader 识别打包css文件,打包到index.js中
{
test:/\.css$/,
use:['style-loader','css-loader']
}
//MiniCssExtractPlugin 将css文件单独抽离出来,不然他会被打包到index.js中
module:{
rules:[{
test:/\.css$/,
use:[
MiniCssExtractPlugin.loader,//将style-loader替换掉
'css-loader'
]
}]
}
//plugin插件要在下边实例化
plugins:[
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
]
3)关于图片、字体文件的打包
url-loader的使用和file-loader类似,特别之处在于:url-loader如果未超过限定的大小,回打包到js文件中,这样一个小的图片文件就不会在发送一次http请求了;如果超过了限定大小,会打包到image文件夹下,这样不会使js文件过大,不会导致页面出现一大段时间的空白。
url-loader插件的安装
module:{
rules:[{
test:/\.(png|jpg|gif)$/,
use:{
loader:'url-loader',
options:{
name:'[name].[ext]',
outputPath:'images/',
limit:0
}
}
}
}
4)html-webpack-plugin dist目录下自动生成html文件并将对应的js文件打包进去
var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins:[
new HtmlWebpackPlugin({
template:'src/view/index.html',
filename:'view/index.html',
inject:true,
hash:true,
chunks:['index']//决定了将哪一个js文件打包到html中,chunks的名字和入口文件js文件名字相同
})
]
如果在需要一个页面就要在重复上面的一堆代码,所以我们封装一个打包引入的函数,这样就只需要写一行就行,每次只传参数就OK了
//做创建多个页面封装一个函数,这样就不用一个页面new一次了
var getHtmlConfig = function(name){
return{
template:'src/view/'+ name +'.html',
filename: '/view/' + name +'.html',
inject:true,
hash:true,
chunks:[name]//通过这个配置让不同的html引入不同的js文件
}
};
module.exports = {
entry:{
'index':'./src/page/index/index.js',
'list':'./src/page/list/index.js',
'detail':'./src/page/detail/index.js',
'cart':'./src/page/cart/index.js',
'order-confirm':'./src/page/order-confirm/index.js',
'payment':'./src/page/payment/index.js',
'user-login':'./src/page/user-login/index.js',
'user-register':'./src/page/user-register/index.js',
'user-pass-reset':'./src/page/user-pass-reset/index.js',
'user-center':'./src/page/user-center/index.js',
'user-center-update':'./src/page/user-center-update/index.js',
'user-pass-update':'./src/page/user-pass-update/index.js',
'result':'./src/page/result/index.js'
},
plugins:[
new HtmlWebpackPlugin(getHtmlConfig('index')),
new HtmlWebpackPlugin(getHtmlConfig('list')),
new HtmlWebpackPlugin(getHtmlConfig('detail')),
new HtmlWebpackPlugin(getHtmlConfig('cart')),
new HtmlWebpackPlugin(getHtmlConfig('order-confirm')),
new HtmlWebpackPlugin(getHtmlConfig('payment')),
new HtmlWebpackPlugin(getHtmlConfig('user-login')),
new HtmlWebpackPlugin(getHtmlConfig('user-register')),
new HtmlWebpackPlugin(getHtmlConfig('user-pass-reset')),
new HtmlWebpackPlugin(getHtmlConfig('user-center')),
new HtmlWebpackPlugin(getHtmlConfig('user-center-update')),
new HtmlWebpackPlugin(getHtmlConfig('user-pass-update')),
new HtmlWebpackPlugin(getHtmlConfig('result'))
]
}
5)html-loader 提取出公共的html部分,在别的html直接引入即可
先安装在使用
//引入的写法
<%= require('html-loader!./layout/nav.html') %>
6)clean-webpack-plugin 清除上一次的打包文件
//注意引入的时候要加 {}
var { CleanWebpackPlugin } = require('clean-webpack-plugin');
7)webpack-dev-server 自动开启一个服务器
8)hogan.js 模板渲染插件
9)webpack-merge 将配置文件进行合并的插件
2.关于模块的导出和导入
如初引入问题关于什么的规范...(背一下)
//导出
var _product = {
.......
}
module.exports = _product;
//导入
var _product = require('service/product-service.js');
二、关于github的一些命令
以前我用的码云,分支能在码云上创建,看来这次是不行了,翻阅资料发现:
创建分支 git branch xxx
将分支提交远程 先切换到主分支git checkout master 然后执行 git push origin xxx
切回xxx分支进行开发,提交代码 git add . git commit -m 'xxx'
然后切回master分支 合并分支 git merge xxx
将合并后的本地内容推送远程 git push
删除本地分支 git branch -D xxx
删除远程分支 git push origin --delete xxx
重新安装node_module文件 npm install
:wq退出合并时弹出的理由
三、工具类util/_mm
1.封装了一个ajax请求,这样就不用每次都写了
规定 res.status === 0为请求成功,res.status === 10为未登录状态,设置自动跳转到登录页面,res.status === 1 为数据请求错误
res.status === 10强制登录时,应该加入重定向,这样就可以让登录界面捕获到是从那个界面跳转过来的
//ajax请求
else if(10 === res.status){
_this.doLogin();
}
// 统一登录处理
doLogin : function(){
window.location.href = './user-login.html?redirect=' + encodeURIComponent(window.location.href);
},
2.获取url参数
//获取url参数
getUrlParam : function(name){
//一直匹配到&符号才结束
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
var result = window.location.search.substr(1).match(reg);
return result ? decodeURIComponent(result[2]) : null;
},
3.这个网站,我们都用hogan渲染html模板
//渲染html模板
renderHtml : function(htmlTemplate,data){
var template = Hogan.compile(htmlTemplate),
result = template.render(data);
return result;
},
4.成功、错误提示,弹出弹框
5.字段的验证,需要传入两个参数,值和需要的类型
四、导航nav(登录、注册、个人中心部分)
1.点击事件:
登录/注册按钮会跳转到相应页面,进入登录页面时要给url重定向传进去是从哪个页面进入的登录界面
2.加载用户信息事件:
首先要给后台发送请求,检验现在得登录状态,如果是已登录状态则将“登录/注册”隐藏掉,将欢迎xxx显示出来,xxx是后台传回来的参数中的_user.checkLogin()
如果是以登录的状态,那么同时也要加载购物车的数量 _cart.getCartCount(),数量的数字也用传回来的参数显示
登录注册部分涉及到一旦页面显示,就会一直监测登录状态,一旦发生改变,就触发显示状态和购物车显示的请求
五、user-login.html用户的登录界面
点击事件:点击提交表单按钮,首先是获取到账号和密码,将账号和密码进行初步验证,设置验证信息,设置标志位
//表单字段的验证
formValidate : function(formData){
var result = {
status : false,
msg : ''
};
if(!_mm.validate(formData.username,'require')){
result.msg = '用户名不能为空';
return result;
}
if(!_mm.validate(formData.password,'require')){
result.msg = '密码不能为空';
return result;
}
//通过验证,返回正确提示
result.status = true;
result.msg = '验证通过';
return result;
}
初步验证通过才可以提交给后台,我们从哪里从里跳出的登录界面,就还返回去哪里(重定向)
if(validateResult.status){
_user.login(formData,function(res){
window.location.href = _mm.getUrlParam('redirect') || './index.html';
},function(errMsg){
formError.show(errMsg);
});
}
六、用户注册
当验证用户名时,鼠标失去焦点,如果用户名为空就不做任何验证,如果不为空便验证后台是否有重复的名字了_user.checkUsername(),同样是初步验证所有字段,设置标志位和信息提示,如果均验证没问题便想后台发请求_user.register(),请求成功后跳转到结果页面,并传入指定的参数,让页面知道显示“xxxx成功!”
七、结果页result.html
根据每次跳转的结果页的url参数来显示不同的div元素,div中显示的是需要的结果信息
$(function(){
var type = _mm.getUrlParam('type') || 'default';
//将jquery对象变成dom对象
var $element = $('.' + type + '-success');
$element.show();
})
八、user-pass-reset找回密码
找回密码分三步-----输入用户名点击下一步》展示密码提示问题输入提示答案点击下一步》输入新密码点击完成
定义一个全局的变量,存储这三步的所有表单数据
1.首先将三个步骤的div都隐藏掉,然后一进页面便加载第一个div loadStepUsername()
输入用户名后,点击第一个div的下一步,发送请求_user.getQuestion()获取用户以前注册时的找回密码提示问题,成功的回调中,将用户名和提示问题都存储在全局变量中,并且进行下一个div的展示loadStepQuestion();
2.然后展示提示问题div,将上一个div隐藏掉loadStepQuestion()
将传回的res提示问题渲染到页面上,点击下一步,将用户名,用户填写的问题答案一并传到后台_user.checkAnswer()回调成功后便执行下边的步骤,
3.展示修改新密码div,将上一个div隐藏掉loadStepPassword()
在判断密码位数大于等于6位时,将全局变量里的用户名和新密码均传递给后台_user.resetPassword(),成功回调时跳转到result页并穿对应参数
九、个人中心的更新密码(修改密码) user-pass-update
页面展示三个框---原密码--新密码--确认密码,获取到前两个框的内容后提交请求_user.updatePassword()成功后,弹出修改成功。
十、首页
首页的index.js中什么也没有设计,只是在每一个跳转的a标签中加入了列表页或者商品详情页的url参数
<a class="link" target="_blank" href="./list.html?keyword=电脑">电脑</a>
<a href="./list.html?categoryId=100007">
将捕获这个url留给了商品列表页和详情页
十一、商品列表页 list.html
1.第一个功能是商品列表的展示
获取到url中的参数,变成全局变量
data : {
listParam : {
// 搜索的关键词/商品分类
keyword : _mm.getUrlParam('keyword') || '',
//直接点击的具体商品的Id
categoryId : _mm.getUrlParam('categoryId') || '',
// 排序的方式
orderBy : _mm.getUrlParam('orderBy') || 'default',
// 想要第几页
pageNum : _mm.getUrlParam('pageNum') || 1,
// 每页多少个商品
pageSize : _mm.getUrlParam('pageSize') || 20
}
},
将这些参数传到后台_product.getProductList(),结合回调传回来的数据,利用hogan模板进行渲染,这样就能做到一个页面,我们可以根据不同的商品id参数,来展示不同的商品列表或者商品详情了!nice!!!!!
并且将获取到的信息重新存储在全局变量中
_this.loadPagination({
// 布尔值,有没有前一页
hasPreviousPage : res.hasPreviousPage,
// 前一页的数字
prePage : res.prePage,
// 布尔值,是否有下一页
hasNextPage : res.hasNextPage,
// 下一页的的数字
nextPage : res.nextPage,
// 当前页是第几页
pageNum : res.pageNum,
// 一共有多少页
pages : res.pages
});
2.让商品按升序/降序,默认顺序排列
给“默认排序”“价格排序”两个按钮点击时切换active类,对于价格排序,要判断现在是desc 还是asc来回进行切换,并且把每次获得的参数添加到全局变量里,然后在执行一次加载列表的函数,列表部分就会重新被加载
bindEvent : function(){
var _this = this;
// 排序按钮的点击
$('.sort-item').click(function(){
var $this = this;
// 不管点了那个排序按钮都从第一页开始展示
_this.data.listParam.pageNum = 1;
//点击默认排序
if($this.data('type') === 'default'){
// 已经是选中状态就什么都不做了
if($this.hasClass('active')){
return;
}
// 如果当前选中的是价格,点击了默认排序后
else{
$this.addClass('active').siblings('.sort-item')
.removeClass('active asc desc');
_this.data.listParam.orderBy = 'default';
}
}
// 点击价格排序
else if($this.data('type') === 'price'){
// active class 的处理
$this.addClass('active').siblings('.sort-item')
.removeClass('active');
// 升序、降序的处理
if(!$this.hasClass('asc')){
$this.addClass('asc').removeClass('desc');
_this.data.listParam.orderBy = 'price_asc';
}else{
$this.addClass('desc').removeClass('asc');
_this.data.listParam.orderBy = 'price_desc';
}
}
//重新加载列表
_this.loadList();
})
},
十二、商品详情页
1.加载商品详情
根据url传过来的id值来判断展示那个商品,将这个参数传到调用的后台请求中_product.getProductDetail(),然后依旧是用获取到的数据进行模板的渲染,将回调回来的关于商品的库存等等都存入到全局变量中
2.图片预览,底下的各种小图片鼠标划过时,上边大图就会变
利用了一个mouseenter事件,没划过一次,便获取到上边的图片标签换一下src
$(document).on('mouseenter','.p-img-item',function(){
var imgUrl = $(this).find('.p-img').attr('src');
$('.main-img').attr('src',imgUrl);
});
3.关于商品数量的点击
判断点击的按钮里面的类是plus还是minus,获取到文本框在原数量的基础上加一或者减一,在通过判断全局变量中的库存数量来判断是否还能在点击+
4.将商品加入购物车中
将全局变量中的商品id和框中的商品数量都传到请求接口_cart.addToCart()操作成功依旧显示result页面
十三、购物车页 cart.html
1.购物车页面的加载
请求接口_cart.getCartList()将传回来的数据进行hogan渲染
2.关于每个商品的选中/取消
//每一个商品的选择/取消
$(document).on('click','.cart-select',function(){
var $this = $(this),
productId = $this.parents('.cart-table').data('product-id');
//选中
if($this.is(':checked')){
_cart.selectProduct(productId,function(res){
// 重新渲染购物车
_this.renderCart(res);
},function(errMsg){
_this.showCartError();
});
}
//取消选中
else{
}
利用$this.is(':checked')来判断当前是否被选中,选中或者取消选中都分别请求接口,并将接口返回的信息再次调用页面加载的那个方法进行购物车页面的渲染。
3.全选/取消全选
全选和取消全选是和单个商品的选择是一样的,都是分别请求不同的接口,返回的信息,进行页面的重新渲染。
4.关于商品的+/-
首先获取到现在得数量值,在判断点击的按钮是+还是 -,设一个新的值来记录数量在原数值基础上加一或者减一,在点击的过程中一直请求接口,将新修改的数值和获取的商品id传到接口中_cart.updateProduct()请求成功的回调中再次调用渲染模板函数渲染页面
5.点击单个商品后面的删除按钮
给一个确认删除的判断,然后请求删除接口,后台直接删除了这个商品
$(document).on('click', '.cart-delete', function(){
if(window.confirm('确认要删除该商品?')){
var productId = $(this).parents('.cart-table')
.data('product-id');
// 删除指定商品
_this.deleteCartProduct(productId);
}
});
6.点击“全选”旁边的“删除选中”按钮
还是同样做一个确认删除的判断,在获取到点击的复选框所在的一整个商品的容器,将他们push到一个数组中,判断,如果有选中的商品,就请求接口,将这个数组传给后台_this.deleteCartProduct()
7.提交购物车
判断一下总价是否大于0 ,大于0便跳转到订单确认页
十四、订单确认页
1.调用接口分别渲染地址列表和商品列表
2.地址的选择/删除以及新地址的添加(将地址的所有功能封装在了一个新的js中,以便维护)
获取到选择的地址,并将其放在全局变量中
原地址的删除,获取到选中的商品的id然后请求接口,重新调用地址渲染接口进行渲染
新地址的添加:新地址编辑后,点击添加可以重复上边那些加载地址的函数执行
3.订单的最终提交
之前存在全局变量的关于商品和地址的信息已参数形式传到接口_order.createOrder(),在回调中进行页面的跳转,并将参数传进url中window.location.href = './payment.html?orderNumber=' + res.orderNo; (即订单号)
十五、支付页面
通过url中获取到的订单参数请求接口_payment.getPaymentInfo()进行支付页面的渲染,每5s便请求接口_payment.getPaymentStatus()检查一下订单的支付状态,如果一旦支付便跳转到result页面,并将参数传过去
if(res == true){
window.location.href = './result.html?type=payment&orderNumber=' + _this.data.orderNumber;
}