研发环境搭建
yarn add global gulp-cli
yarn add gulp -D
创建gulpfile.js
const {src,series,dest,watch,parallel}=require('gulp')
//series,按顺序依次执行,parallel并行
const sass=require('gulp-sass')//编译scss的插件
const concat=require('gulp-concat')//
const gulpServer=require('gulp-webserver')
const webpackStream=require('webpack-stream')
const proxy=require('http-proxy-middleware')
const path=require('path')
function copyHtml(cb){
return src('./src/views/**/*.html')//匹配所有得html
.pipe(dest('./dev/'))//目标地址
cb();
}
function compileCss(){
return src('./src/style/**/*.scss')
.pipe(sass().on('error',sass.logError))
.pipe(concat('all.css'))
.pipe(dest('./dev/'))
}
function compileJs(){
// return src('./src/js/**/*.js')
// .pipe(dest('./dev/'))
return src('./src/js/**/*.js')
.pipe(webpackStream({
mode:'development',
entry:{
main:'./src/js/app.js',
detail:'./src/js/Detail.js',
},
devtool:'#inline-source-map',//网页显示源代码,方便调试
output:{
//完整的输出目录
path:path.resolve(__dirname,'./dev/'),
filename:'[name].js'
},
//转es5
module:{
rules:[ //规则
{
test:/\.js$/,//.js的文件进行处理
exclude:/node_modules/,//排除选项文件夹
use:{
loader:'babel-loader',
options:{
presets: ['@babel/preset-env'],//预设
plugins: ['@babel/plugin-transform-runtime']//插件程序
}
}
},{
test:/\.html$/,
loader:'string-loader'
}
]
}
}))
.pipe(dest('./dev/'))
}
function startServer(){
return src('./dev').pipe(gulpServer({
port:8008,
host:'127.0.0.1',
//是否支持热更新
livereload: true,
directoryListing: false,
open: true,
middleware:[
proxy('/api',{
target:'http://localhost:3003/polistionlist',//代理的目标地址
changeOrigin:true,//是否支持跨越
/* pathRewrite:{
'^/api':''
} */
})
]
}))
}
function copyLibs(){
return src('./src/libs/**/*.*')
.pipe(dest('./dev/libs/'))
}
function copyImg(cb){
return src('./src/image/**/*.*')//匹配所有得html
.pipe(dest('./dev/image/'))//目标地址
cb();
}
function watchFile(){
watch('./src/style/**/*.scss',(cb)=>{
compileCss()
cb()
})
watch('./src/js/**/*.js',(cb)=>{
compileJs()
cb()
})
watch('./src/views/**/*.html', (cb) => {
copyHtml();
compileJs();
cb();
})
}
exports.default=series(parallel(copyHtml,copyLibs,copyImg),compileCss,compileJs,
startServer,watchFile)
执行gulp 就可以
安装yarn add gulp-webserver -D//搭建研发的server
yarn add node-sass gulp-sass -D//编译scss
yarn add webpack-stream -D//编译js文件
yarn add babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/runtime //一些es678的转化为es5
yarn add string-loader -D
移动端一像素适配
//sass混合器;
如果你的整个网站中有几处小小的样式类似(例如一致的颜色和字体),那么使用变量来统一处理这种情况是非常不错的选择。但是当你的样式变得越来越复杂,你需要大段大段的重用样式的代码,独立的变量就没办法应付这种情况了。你可以通过sass的混合器实现大段样式的重用。
混合器使用@mixin标识符定义。看上去很像其他的CSS @标识符,比如说@media或者@font-face。这个标识符给一大段样式赋予一个名字,这样你就可以轻易地通过引用这个名字重用这段样式。下边的这段sass代码,定义了一个非常简单的混合器,目的是添加跨浏览器的圆角边框。
@mixin border_1px($color) {
position: relative;
//可否区分屏幕
@media(-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5) {
&::before{
content: '';
position: absolute;
left: 0px;
top: 0px;
background-color: $color;
transform: scaleY(0.75);//缩放一半
height: 1px;
width: 100%;
}
}
@media(-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3) {
&::before{
content: '';
position: absolute;
left: 0px;
top: 0px;
background-color: $color;
transform: scaleY(0.33);//缩放一半
height: 1px;
width: 100%;
}
}
@media(-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2) {
&::before{
content: '';
position: absolute;
left: 0px;
top: 0px;
background-color: $color;
transform: scaleY(0.5);//缩放一半
height: 1px;
width: 100%;
}
}
}
移动端特有的touch事件
touch事件与click事件同时触发
touchstart=>touchend=>click(200-300ms延迟)
可以通过event.preventDefault()
touchstart事件是移动端特有事件
touchmove手指移出target元素,则touchmove会被一直触发,需要考虑事件节流,(移动端计算资源非常宝贵)
touchend和touchmove打印的e.target的内容是一样
touchcancel模态对话框,(电话)就会调用这个事件
触摸事件的对象
TouchEvent:
<script>
window.onload=function(){
var box=document.querySelector('div')
box.addEventListener('touchstart',function(e){
console.dir(e)
})
}
</script>
TouchList
window.onload=function(){
var box=document.querySelector('div')
var p=document.querySelector('p')
box.addEventListener('touchend',function(e){
//返回touch对象的个数,移动端就可以获取几根手指触摸的事件
p.innerHTML=e.changedTouches.length
for (let i = 0; i < e.changedTouches.length; i++) {
console.log(e.changedTouches.item(i))
}
})
}
Touch
clientX: 234.40000915527344//触摸点相对于浏览器(视图窗口)左边的坐标
clientY: 24.199996948242188
force: 0//按压力度
identifier: 0//每一个touch对象唯一的id,通过它追踪每一个touch对象
pageX: 234.40000915527344//触摸点相对于document左边的坐标(会包含滚动条的距离)
pageY: 24.199996948242188
radiusX: 11.5
radiusY: 11.5
rotationAngle: 0
screenX: 717.6000366210938//触摸点相对于屏幕左边的坐标
screenY: 236
合作模式,模拟数据
yarn aglobal add json-server
启动json-server服务:json-server ./mock/data.json
//也可以单独配置
json-server ./mock/data.json -p 3333(端口) -w(观测变化) i(指定数据) id -s(静态资源目录)
可以直接在package.json里面配置,执行npm run mock就可以
如果有其他路径,可以配置
routes.json文件
入口页面
最后在package.json里面配置data
当数据模拟成功,使用模板引擎
npm install art-template --save或者下载文件
传输数据:
以$value来接受
//https://better-scroll.github.io/docs/zh-CN/guide/how-to-install.html#npm
移动端下拉刷新,安装插件better-scroll
yarn add @better-scroll/core@next
//下拉刷新
yarn add @better-scroll/pull-down@next
//上拉加载更多
yarn add @better-scroll/pull-up@next
要求:唯一的子节点,子节点必须超出包裹的高度,html内容必须已经渲染完
//配置下拉刷新,需要下拉的外容器
this.scroll=new BScroll('#position-wrapper',{
scrollY:true,
pullDownRefresh:{
threshold:40,//下拉40触发事件
stop:0//当下来恢复原位需要手动设置0,默认40的高度
}
})
//下拉刷新
this.scroll.on('pullingDown',async()=>{
await this.getlist()//获取数据
// 每次触发下拉事件后,在回调函数的最后,都应该调用 finishPullDown()
this.scroll.finishPullDown();
})
//检测实时滚动
this.scroll.on('scroll',function(){
if(this.y>20){
$(".refersh").show()
}else{
$(".refersh").hide()
}
})
//分页条件
http://localhost:3003/polistionlist?_page=0&_limit=3
refresh()
参数:无
返回值:无
作用:重新计算 BetterScroll,当 DOM 结构发生变化的时候务必要调用确保滚动的效果正常。
//路由切换,地址栏的前进和后退
//三个页面的点击,添加属性data-url
<ul id="nav">
<li class="active" data-url="position">
<i class="iconfont">
</i>职位</li>
<li data-url="search">
<i class="iconfont">
</i>搜索</li>
<li data-url="main">
<i class="iconfont">
</i>我的</li>
</ul>
//----------------------
//入口文件app.js
import Router from '../js/router/router'//路由的文件
const MODE='hash';
// const MODE='history';
class App{
constructor(){
this.router=new Router({
mode:MODE//哈希或者hisory的方式传过去
})
this.initEvent()
}
initEvent(){
var self=this
$("#nav li").on('click',function(){
$(this).addClass('active').siblings().removeClass('active')//样式切换
let url=$(this).attr('data-url')//取到url
self.router.go(url)//点击后执行router下面的go方法,将url传过去
})
//----------------------
// -----页面强制刷新,不会跳转
addEventListener('load',()=>{
let url=location.hash.replace('#','')||'position';
if(MODE==='history'){
url=history.state?history.state.path:'position';
}
this.router.go(url)
this.setActive(url)
})
// -----
//前进和后退
if(MODE==='hash'){
addEventListener('hashchange',()=>{
let hash=location.hash.replace('#','');
self.router.go(hash)
this.setActive(hash)
})
}else{
$(window).on('popstate',()=>{
console.log(history.state)
let url=history.state.path;
self.router.go(url)
this.setActive(url)
})
}
//激活哪个active
setActive(url){
$('#nav').find('li[data- url="'+url+'"]').addClass('active').siblings().removeClass('active')
}
}
router.js文件
import position from '../controller/positionController'
import search from '../controller/searchController'
import main from '../controller/profileController'
//-------以上是三个页面
class Router{
constructor(options){
this.mode=options.mode;
console.log(options)//hash或者history
this.routes={
//三个路由
'position':position,
'search':search,
'main':main//前面的key必须和data-url设置的一样
}
}
go(path){
if(this.mode==="hash"{
location.hash=path;//地址栏加入path
}else{
history.pushState({path},'',"?"+path)
}
this.loadview(path)//然后执行去向点击的页面
}
loadview(path){
if(this.routes[path]){
this.routes[path].render()//然后执行去向点击的页面
}else{
//404 找不到
}
}
}
swiper左右滑动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
//引入的css
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css">
<style>
.swiper-container {
width: 600px;
height: 300px;
}
</style>
</head>
<body>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
<!-- 如果需要分页器 -->
<!-- <div class="swiper-pagination"></div> -->
<!-- 如果需要导航按钮 -->
<!-- <div class="swiper-button-prev"></div> -->
<!-- <div class="swiper-button-next"></div> -->
<!-- 如果需要滚动条 -->
<!-- <div class="swiper-scrollbar"></div> -->
</div>
<!-- 导航等组件可以放在container之外 -->
//引用的js
<script src="https://unpkg.com/swiper/js/swiper.min.js"> </script>
<script>
var mySwiper = new Swiper ('.swiper-container', {
// direction: 'vertical', // 垂直切换选项
loop: true, // 循环模式选项
// 如果需要分页器
// pagination: {
// el: '.swiper-pagination',
// },
// 如果需要前进后退按钮
// navigation: {
// nextEl: '.swiper-button-next',
// prevEl: '.swiper-button-prev',
// },
// 如果需要滚动条
// scrollbar: {
// el: '.swiper-scrollbar',
// },
})
</script>
</body>
</html>
//配置多页面
entry:{
main:'./src/js/app.js',
detail:'./src/js/detail.js',//详情页面
},
devtool:'#inline-source-map',//网页显示源代码,方便调试
output:{
//完整的输出目录
path:path.resolve(__dirname,'./dev/'),
filename:'[name].js'
},
当使用better-scroll时候会阻止原生js的一些事件所以配置时候需要将click设置为true
//项目打包
//生产环境的配置
const {src,series,dest,watch,parallel}=require('gulp')
//series,按顺序依次执行,parallel并行
const sass=require('gulp-sass')//编译scss的插件
const concat=require('gulp-concat')//
const webpackStream=require('webpack-stream')
const del=require('del')
const path=require('path')
const rev=require('gulp-rev')
const revCol=require('gulp-rev-collector')
function copyHtml(cb){
return src('./src/views/**/*.html')//匹配所有得html
.pipe(dest('./dist/'))//目标地址
cb();
}
function compileCss(){
return src('./src/style/**/*.scss')
.pipe(sass().on('error',sass.logError))
.pipe(concat('all.css'))
.pipe(rev())
.pipe(dest('./dist/'))
.pipe(rev.manifest())
.pipe(dest('./rev/'))//生成一个json文件,原来的css和现在的css
}
function compileJs(){
// return src('./src/js/**/*.js')
// .pipe(dest('./dev/'))
return src('./src/js/**/*.js')
.pipe(webpackStream({
mode:'production',
entry:{
main:'./src/js/app.js',
detail:'./src/js/Detail.js',
},
output:{
//完整的输出目录
path:path.resolve(__dirname,'./dist/'),
filename:'[name].js'
},
//转es5
module:{
rules:[ //规则
{
test:/\.js$/,//.js的文件进行处理
exclude:/node_modules/,//排除选项文件夹
use:{
loader:'babel-loader',
options:{
presets: ['@babel/preset-env'],//预设
plugins: ['@babel/plugin-transform-runtime']//插件程序
}
}
},{
test:/\.html$/,
loader:'string-loader'
}
]
}
}))
.pipe(rev())
.pipe(dest('./dist/'))
.pipe(rev.manifest())
.pipe(dest('./rev/'))
}
function copyLibs(){
return src('./src/libs/**/*.*')
.pipe(dest('./dist/libs/'))
}
function copyImg(cb){
return src('./src/image/**/*.*')//匹配所有得html
.pipe(dest('./dist/image/'))//目标地址
cb();
}
function remove(){
return del('./dist/')
}
function revColl(){
//替换文件,将打包里面的dist的html,替换生成的哈希文件
return src(['./rev/*.json','./dist/*.html'])
.pipe(revCol())
.pipe(dest('./dist/'))
}
exports.default=series(remove,parallel(copyHtml,copyLibs,copyImg),compileCss,compileJs,revColl)
配置package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"mock": "json-server ./mock/positionlist.json -w -p 3003",
"data": "json-server ./mock/mock.js -r ./mock/routes.json -p 8088",
"dev":"gulp -f ./gulpfile.dev.js",
"build":"gulp -f ./gulpfile.pro.js"
},
执行yarn build
如果文件太大,需要调小
比方第三方插件的引用可以换一种方式导入
// import BScroll from '@better-scroll/core'
// import PullDown from '@better-scroll/pull-down'
// import Pullup from '@better-scroll/pull-up'
//不使用import导入
//去下载原来的batter-scroll.js文件,在入口页面导入
部署之前删除目录下的文件
yarn add del -D
function remove(){
return del(’./dist/’)
}
文件名后面加随机hash值,防止浏览器缓存问题,当文件有改动,用户访问时候新的路径便不会缓存
插件gulp-rev
yarn add gulp-rev -D
yarn add gulp-rev-collector -D
//列如css文件
function compileCss(){
return src('./src/style/**/*.scss')
.pipe(sass().on('error',sass.logError))
.pipe(concat('all.css'))
.pipe(rev())
.pipe(dest('./dist/'))
.pipe(rev.manifest())
.pipe(dest('./rev/'))//生成一个json文件,原来的css和现在的css
}
function revColl(){
//替换文件,将打包里面的dist的html,替换生成的哈希文件
return src(['./rev/*.json','./dist/*.html'])
.pipe(revCol())
.pipe(dest('./dist/'))
}
生成的json文件
部署到nginx
nginx是反向代理服务器,跨平台,配置简单,内存消耗小。官方支持五万并发,支持GZP压缩,通信机制采用epoll模型
动态资源,静态资源分离
易于性能优化。
安装nginx
在mac上:brew install nginx
默认安装目录:/usr/local/etc/nginx/
nginx //启动服务
nginx -s stop//停止服务
nginx -s reload//重启服务
在windows:
官网下载安装包,解压之后
然后双击打开nginx.exe当在地址栏输入localhost显示下图就成功打开
打开conf文件夹.配置
然后再指定目录下建一个conf.d文件夹,里面再建一个**.conf文件,配置
server{
listen 9999;
#用来IP地址或者域名,多个域名用空格隔开
server_name localhost;
#文件路径
root D:\1000phone\third-one\dist;
#默认访问页面
index index.html;
location /api{
#路径重写
rewrite /api/(.*) /$1 break;
#代理的目标地址
proxy_pass http://127.0.0.1:3003/;
}
}
项目最后效果