进入一家老的公司,公司规模也不大,但是我们从git上下载后,我们猛然发现,这项目有一种心痛的感觉。
这都2020年了,居然还有人用这么古老的方案去进行一个项目。那么这时候,我们该怎么办,我们陷入了沉思,继续维护?还是准备跑路?忍住,我们先看看代码…
目录 一个公共的index代码…居然都没有抽离…如下代码大概有个1000多行…
blockquote,body,dd,dl,dt,fieldset,form,h1,h2,h3,h4,h5,h6,hr,html,iframe,input,legend,li,ol,p,pre,td,textarea,th,ul{
padding:0;margin:0}
html{
-webkit-overflow-scrolling:touch;-webkit-text-size-adjust:100%;font-family:Arial, Helvetica, sans-serif;}
body{
-webkit-overflow-scrolling:touch;-webkit-box-sizing:border-box;box-sizing:border-box}
a,body,select,select:focus,textarea,textarea:focus{
-webkit-tap-highlight-color:transparent;outline:0;-webkit-appearance:none}
li{
list-style-type:none}
table{
border-collapse:collapse;border-spacing:0}
fieldset{
border:none}
legend{
display:none}
a:active,a:hover,button{
outline:0}
input[type=checkbox],input[type=radio]{
-webkit-box-sizing:border-box;box-sizing:border-box}
b,em,i{
font-style:normal;font-weight:400}
a{
text-decoration:none;-webkit-tap-highlight-color:transparent}
@media screen and (min-width:1440px){
html{
font-size:200%}}
@media screen and (max-width:1440px){
html{
font-size:200%}}
@media screen and (max-width:1024px){
html{
font-size:150%}}
@media screen and (max-width:980px){
html{
font-size:150%}}
@media screen and (max-width:750px){
html{
font-size:150%}}
@media screen and (max-width:720px){
html{
font-size:150%}}
@media screen and (max-width:640px){
html{
font-size:150%}}
@media screen and (max-width:540px){
html{
font-size:150%}}
@media screen and (max-width:480px){
html{
font-size:125%}}
@media screen and (max-width:432px){
html{
font-size:120%}}
@media screen and (max-width:414px){
html{
font-size:115%}}
@media screen and (max-width:400px){
html{
font-size:112.5%}}
@media screen and (max-width:393px){
html{
font-size:104%}}
@media screen and (max-width:375px){
html{
font-size:104%}}
@media screen and (max-width:360px){
html{
font-size:100%}}
@media screen and (max-width:320px){
html{
font-size:87.5%}}
@media screen and (max-width:240px){
html{
font-size:75%}}
body{
background-color: #f7f7f7;}
.popBox,.popBind,.popBind_text,.popBind_error{
position: fixed;width:100%;height:100%;background:rgba(0,0,0,0.5);color:#999999;display: none;top:0px;z-index:10;}
.popBoxCont{
width:18.1875rem;background-color: #fff;border-radius:0.3125rem;position: absolute;left:50%;top:50%;transform:translate(-50%,-50%);padding-bottom:2.0313rem;}
.popBox_top{
font-size:1rem;line-height:1.2rem;margin-top:2.75rem;margin-bottom:1.1875rem;}
.popBox_top i{
font-size:1.3125rem;line-height:1.5rem;vertical-align:bottom;margin-left:0.4375rem;color:#5bba48;font-weight:bold;}
.popBoxDetail p{
margin-left:2.375rem;margin-right:2.375rem;}
.popBoxDetail .popBox_counseName{
font-size:1rem;line-height:1.3125rem;color:#5bbb47;background-color: #edfbea;margin-bottom:1.6563rem;position: relative;margin-left:1.9688rem;margin-right:1.9688rem;padding:0.2375rem 0.4688rem;}
不能慌张,我们可是前端工程师…那么我们该怎么办呢?首先我们先分析一下我们可以怎么办,那么我们首先分析一下他使用的技术栈和运用场景。
通过和项目组,产品的沟通。该项目,是运行在微信公众号上的的一个h5页面。那么能够给你的时间,差不多是一周时间,去熟悉了解,整个项目。
1. js架构使用技术栈
1.1 jquery
1.2 jweixin
1.3 swiper.min
2. css解决方案
纯手写,手动rem
其实这时候,不难发现,就是累加的js,http未封装状态...
方法 | 方案 | 缺点 |
---|---|---|
文件夹隔离 | 将老代码,丢到我看不到的地方,继续开发新项目。(看不到,那就当做没有问题) | 在老项目的代码,硬伤还是硬伤,新代码的架构被迫跟随 |
微前端 | 做一个大规模容器,将新老项目做一个中间的桥接,让主架构负责项目的沟通,保证2个服务器正常运行 | 微前端的技术方案,大部分实例是作为后端管理系统中运行,在手机端中的适配能力未知。 |
webpack | 多文件打包方案,让新老代码,在工具中兼容 | 人力改代码 |
webpack的优点不言而喻,如果不清楚的,可以去看webpack官网的介绍
本质上,webpack 是一个现代 JavaScript 应用程序的_静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个_依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
首先我们肯定是不会想去想改动,老项目代码,那毕竟是老项目…你去改动了…万一东西墙崩溃…最后的事故可不小。那么新页面呢…我们肯定是希望去使用单页面技术,毕竟加载和体验上,都有了毕竟好的体验。
先将原来的文件复制出一个来,我们也不希望在改动的过程中,破坏了代码原本的功能
mkdir jq-webpack
yarn add [email protected] [email protected] -D
touch webpack.config.js
这时候,项目需要将 package.json 添加上指令
"build": "webpack --mode production --config=webpack.config.js",
"server": "webpack-dev-server --hot --config=webpack.config.js"
然后开始配置 webpack.config.js 原则上可以配置多种环境但是我们这边为了简单就配置一种
在项目结构不算过于复杂的情况下,其实我们还是可以理一下思路,就比如项目中,其实初始化css,js 都是可以抽离出来并且,可以形成一套完整的路由体系。那么就可以制作一个项目抽离的目录结构
- router
- index.js 组合文件
- resource.js 资源文件
- router.js 路由文件
- src
- common 公用的部分
- css
- js
- images
- pages 老项目的对应关系
- index
- index.html
- index.js
- index.css
- activity
- index.html
- index.js
- index.css
- ....
- utils 工具库
index.js
- public 难以做处理文件
- images
- lib
- package.json
- webpack.config.js
{
"name": "webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --mode production --config=webpack.config.js", // 打包指令
"server": "webpack-dev-server --hot --config=webpack.config.js", // 启动指令
"upload-test": "NODE_ENV=test node ./deploy", // 自动化上传-测试
"upload-prod": "NODE_ENV=prod node ./deploy" // 自动化上传-正式
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^9.1.0",
"babel-plugin-import": "^1.13.3",
"chalk": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"compression-webpack-plugin": "^6.0.0",
"copy-webpack-plugin": "^4.6.0",
"css-loader": "^3.3.0",
"cssnano": "^4.1.10",
"expose-loader": "1.0.3",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "4.5.0",
"html-withimg-loader": "^0.1.16",
"less": "^3.13.0",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^1.3.2",
"optimize-css-assets-webpack-plugin": "^5.0.0",
"ora": "^5.1.0",
"post-loader": "^2.0.0",
"postcss-loader": "^2.1.1",
"postcss-pxtorem": "^5.0.0",
"postcss-safe-parser": "^5.0.2",
"progress-bar-webpack-plugin": "^2.1.0",
"scp2": "^0.5.0",
"style-loader": "^1.0.0",
"url-loader": "^4.1.1",
"vue-loader": "^15.9.5",
"vue-template-compiler": "^2.6.12",
"webpack": "4.19.1",
"webpack-cleanup-plugin": "0.5.1",
"webpack-cli": "^2.1.4",
"webpack-dev-server": "3.11.0"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"lib-flexible": "^0.3.2",
"vant": "^2.11.2"
}
}
我们使用的技术,是比较普遍的webpack,多页面技术我们可以看到,每一个老项目的html 中,都会引入关于jq、自己的index.js、然后一股脑的images,又或许有些是放在自己的images里面…放在我们开始做一些隔离,分类组合,开始开多个文件夹,放入html、js、css。样式将会比较清楚,这时候开始做一些外部引入的操作。
首先我们配置一下router的文件,我这边做了一些拆分,当然你如果页面能模块化的,建议拆分的更细。
resource.js 将文件路径中的设置放入资源管理库中
const entry = {
// 首页
"index-css": "./src/index/index.css",
"index-js": "./src/index/index.js",
// 活动页
"activity-css": "./src/pages/activity/index.css",
"activity-js": "./src/pages/activity/index.js",
}
module.exports = {
entry
};
router.js 在路由页面中去组合
const router = [
{
name: "首页",
filename: "index.html",
chunks: ["index-css", "index-js"], // 如果多个可以引入多个
template: "./src/pages/index/index.html",
},
{
name: "活动",
filename: "activity.html",
chunks: ["activity-css", "activity-js"],
template: "./src/pages/activity/index.html",
},
];
module.exports = {
router
};
index.js
const htmlPlugin = require("html-webpack-plugin");
const resource = require("./resource");
const routerObj = require("./router");
const htmlWebpackPlugins = [];
routerObj.router.forEach(item => {
htmlWebpackPlugins.push(
new htmlPlugin({
filename: item.filename, //打包后的文件名
minify: false,
chunks: item.chunks, //每个html只引入对应的js和css
inject: true,
hash: true, //避免缓存js。
template: item.template
})
);
});
module.exports = {
htmlWebpackPlugins,
entry: resource.entry
};
当然对于html也需要做一些处理处理,这里有两种方法
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>活动页面title>
head>
<body>
<script src="./public/lib/jquery-1.8.0.min.js" charset="utf-8">script>
body>
html>
这里是一段艰苦的历程…
const path = require("path");
const HtmlRouter = require("./router/index");
const CopyPlugin = require("copy-webpack-plugin");
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
const optimizeCss = require("optimize-css-assets-webpack-plugin");
const {
CleanWebpackPlugin } = require("clean-webpack-plugin"); // 清除dist
const CompressionPlugin = require("compression-webpack-plugin");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
module.exports = {
devServer: {
contentBase: path.resolve("dist"),
host: "localhost", //服务器的IP地址,这里先使用loaclhost地址
compress: true, //服务端压缩是否开启
port: "8888", //配置服务端口号
stats: "errors-only",
historyApiFallback: true,
overlay: true
},
entry: HtmlRouter.entry,
output: {
path: path.resolve("dist"),
filename: "js/[name].[hash:8].js"
},
plugins: [
new ProgressBarPlugin(),
new CleanWebpackPlugin(),
new optimizeCss({
cssProcessor: require("cssnano"), //引入cssnano配置压缩选项
cssProcessorOptions: {
discardComments: {
removeAll: true }
},
canPrint: true //是否将插件信息打印到控制台
}),
new ExtractTextWebpackPlugin({
filename: "css/[name].[hash:8].css", // 配置提取出来的css名称
allChunks: true
}),
new CopyPlugin(
[
{
from: path.resolve(__dirname, "./src/public/lib"),
to: path.resolve(__dirname, "./dist/public/lib")
},
{
from: path.resolve(__dirname, "./src/public/images"),
to: path.resolve(__dirname, "./dist/public/images")
}
],
{
ignore: [], copyUnmodified: true }
),
new CompressionPlugin()
].concat(HtmlRouter.htmlWebpackPlugins),
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"~": path.resolve(__dirname, "src/pages/vue-template")
}
},
module: {
rules: [
{
test: /\.(htm|html)$/i,
loader: "html-withimg-loader"
},
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader"
}
],
publicPath: "../"
})
},
{
test: /\.(png|jpg|gif|jpeg|svg)$/i,
use: [
{
loader: "url-loader",
options: {
//当加载的图片小于limit时,会将图片编译成base64字符串的形式,
//当图片大于这个limit,会用file-loader进行加载
limit: 10000,
//在webpack4.x必须显式的指定fallback备用方法,这里指定为file-loader
fallback: require.resolve("file-loader"),
encoding: "base64",
outputPath: "images/",
publichPath: "images/",
name: "[name].[hash:8].[ext]",
esModule: false //解决方法
}
}
]
}
]
}
};
在处理这一段代码的时候,最令人无奈的就是关于,jq中插入过html,你所有的语法是$(’.xx’).html(xx),在这个阶段,你很容易会遇到一个巨大的坑…就是图片无法被webpack去解析,这样打包出来的图片。
有三种解决方案的思路
前面做了那么多业务,目的就是从业务上可以往vue页面靠齐…那么肯定不会是以vue-cil 这样方式出现,那么我就要研究一下vue-cil的本质,其实还是一个webpack,那么为什么可以解析vue,less。既然是多页面了,又怎么兼容?
熟悉的项目格式又回来了,这里就不多做介绍了
- src
- pages
- vue-template
- index.html
- pages
- 404.vue
- home.vue
- routers
- index.js
- App.vue
在我们刚刚的路由中,我们设置一下
resource.js 资源文件中增加
"vue-template-js": "./src/pages/vue-template/main.js"
router.js 路由文件中增加
{
name: "vue-template",
filename: "template.html",
chunks: ["babel-polyfill", "vue-template-js"],
template: "./src/pages/vue-template/index.html",
}
首先我们需要加载less,然后将我们熟悉的px,自动转rem、自动加上兼容前缀
const VueLoaderPlugin = require("vue-loader/lib/plugin");
plugins: [
...
new VueLoaderPlugin()
]
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"~": path.resolve(__dirname, "src/pages/vue-template"),
vue$: "vue/dist/vue.esm.js"
}
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
},
{
loader: "postcss-loader",
},
],
publicPath: "../",
}),
},
{
test: /\.less$/,
use: ExtractTextWebpackPlugin.extract({
use: [
{
loader: "css-loader",
},
{
loader: "postcss-loader",
},
{
loader: "less-loader",
},
],
fallback: "style-loader",
}),
},
{
test: /\.vue$/,
loader: "vue-loader",
},
],
},
externals: {
vue: "Vue",
"vue-router": "VueRouter"
}
postcss.config.js
module.exports = {
plugins: {
autoprefixer: {
overrideBrowserslist: ["Android >= 4.0", "iOS >= 7"]
},
"postcss-pxtorem": {
rootValue: 37.5,
propList: ["*"]
}
}
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
/>
<title>vue模板</title>
</head>
<style>
html,
body,
#app {
height: 100%;
margin: 0;
padding: 0;
}
.webpack-home {
background-color: #303133;
height: 100%;
display: flex;
flex-direction: column;
}
.webpack-home__main {
user-select: none;
width: 100%;
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.webpack-home__footer {
width: 100%;
flex-grow: 0;
text-align: center;
padding: 1em 0;
}
.webpack-home__footer > a {
font-size: 12px;
color: #ababab;
text-decoration: none;
}
.webpack-home__loading {
height: 32px;
width: 32px;
margin-bottom: 20px;
}
.webpack-home__title {
color: #fff;
font-size: 14px;
margin-bottom: 10px;
}
.webpack-home__sub-title {
color: #ababab;
font-size: 12px;
}
.ql-editor {
min-height: 150px;
}
.ql-snow .ql-picker {
height: 36px !important;
}
@media only screen and (-webkit-min-device-pixel-ratio: 3),
only screen and (min--moz-device-pixel-ratio: 3),
only screen and (-o-min-device-pixel-ratio: 3/1),
only screen and (min-device-pixel-ratio: 3),
only screen and (min-resolution: 458dpi),
only screen and (min-resolution: 3dppx) {
.van-tabbar--fixed {
padding-bottom: 15px !important;
}
}
</style>
<body>
<div id="app">
<div class="webpack-home">
<div class="webpack-home__main">
<img
class="webpack-home__loading"
src="./svg/loading-spin.svg"
alt="loading"
/>
<div class="webpack-home__title">
正在加载资源
</div>
<div class="webpack-home__sub-title">
初次加载资源可能需要较多时间 请耐心等待
</div>
</div>
<div class="webpack-home__footer"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</body>
</html>
实例化一个vue项目,这里只做一个简单的实例化配置,并添加路由
更多可以根据业务情况来配置,比如按需加载什么的,这里只是一个简单的阐述结构
import routers from "./routers/index";
import App from "~/App.vue";
import Vant from "vant";
import "vant/lib/index.css";
import "lib-flexible";
Vue.use(VueRouter);
Vue.use(Vant);
const router = new VueRouter({
routes: routers
});
new Vue({
router,
render: h => h(App)
}).$mount("#app");
在deploy目录下,设置
products.js 服务器配置
/*
*定义多个服务器账号 及 根据 SERVER_ID 导出当前环境服务器账号
*/
const SERVER_LIST = [
{
id: 0,
name: "A-测试环境",
host: "127.0.0.1", // ip
url: "http://www.baidu.com",
port: 22, // 端口
username: "root", // 登录服务器的账号
password: "", // 登录服务器的账号
path: "" // 发布至静态服务器的项目路径
}
];
module.exports = SERVER_LIST;
index.js 执行函数
const scpClient = require("scp2");
const ora = require("ora");
const chalk = require("chalk");
const servers = require("./products");
let server = servers[process.env.NODE_ENV === "prod" ? 1 : 0];
const spinner = ora(
"正在发布到" +
(process.env.NODE_ENV === "prod" ? "生产" : "测试") +
"服务器..."
);
var Client = require("ssh2").Client;
var conn = new Client();
conn
.on("ready", function() {
// rm 删除dist文件,\n 是换行 换行执行 重启nginx命令 我这里是用docker重启nginx
let dels = `rm -rf ${
server.path}\n mkdir ${
server.path}`;
conn.exec(dels, function(err, stream) {
if (err) throw err;
stream
.on("close", function(code, signal) {
// 在执行shell命令后,把开始上传部署项目代码放到这里面
spinner.start();
scpClient.scp(
"dist/",
{
host: server.host,
port: server.port,
username: server.username,
password: server.password,
path: server.path,
},
function(err) {
spinner.stop();
if (err) {
console.log(chalk.red("发布失败.\n"));
throw err;
} else {
console.log(
chalk.green(
"Success! 成功发布到" +
(process.env.NODE_ENV === "prod" ? "生产" : "测试") +
"服务器! \n"
)
);
console.log(server.url);
}
}
);
conn.end();
})
.on("data", function(data) {
console.log("STDOUT: " + data);
})
.stderr.on("data", function(data) {
console.log("STDERR: " + data);
});
});
})
.connect({
host: server.host,
port: server.port,
username: server.username,
password: server.password,
});
https://github.com/MYQ1996/jq-webpack.git