LI WENHAO

init rf-blog project

init rf-blog db config
init rf-blog vue config
init rf-blog node config
create dev-admin\dev-client
Showing 60 changed files with 603 additions and 0 deletions
1 +{
2 + "presets": [
3 + "es2015",
4 + [
5 + "env",
6 + {
7 + "modules": false,
8 + "targets": {
9 + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
10 + }
11 + }
12 + ],
13 + "stage-2"
14 + ],
15 + "plugins": ["transform-runtime"],
16 + "env": {
17 + "test": {
18 + "presets": ["env", "stage-2"]
19 + }
20 + }
21 +}
1 +MIT License
2 +
3 +Copyright (c) 2021 rf
4 +
5 +Permission is hereby granted, free of charge, to any person obtaining a copy
6 +of this software and associated documentation files (the "Software"), to deal
7 +in the Software without restriction, including without limitation the rights
8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 +copies of the Software, and to permit persons to whom the Software is
10 +furnished to do so, subject to the following conditions:
11 +
12 +The above copyright notice and this permission notice shall be included in all
13 +copies or substantial portions of the Software.
14 +
15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 +SOFTWARE.
1 +"use strict";
2 +
3 +process.env.NODE_ENV = "production";
4 +const path = require("path");
5 +const webpack = require("webpack");
6 +const config = require("./webpack.prod.conf");
7 +const compiler = webpack(config);
8 +// 以包的形式包装rm -rf命令,删除文件夹
9 +const rm = require("rimraf");
10 +// 美化node控制台进度
11 +const ora = require("ora");
12 +const spinner = ora({
13 + text: "building for production loading...",
14 + color: "yellow",
15 + spinner: { interval: 80, frames: ["-", "+", "-"] },
16 +});
17 +// 开启loading动画
18 +spinner.start();
19 +// 美化控制台输出
20 +const chalk = require("chalk");
21 +const isAdmin = process.env.NODE_ENV_TYPE === "admin";
22 +// 打包前先删除之前可能已打包过的文件
23 +const rmFile = path.resolve(
24 + __dirname,
25 + `../puclic/${isAdmin ? "admin" : "client"}`
26 +);
27 +
28 +rm(rmFile, (rmErr) => {
29 + if (rmErr) throw rmErr;
30 + compiler.run((err, stats) => {
31 + spinner.stop();
32 + if (err) {
33 + throw err;
34 + }
35 + if (stats.hasErrors()) {
36 + console.log(chalk.redBright("Build failed with errors.\n"));
37 + process.exit(1);
38 + }
39 + process.stdout.write(
40 + stats.toString({
41 + colors: true,
42 + modules: false,
43 + children: false,
44 + chunks: false,
45 + chunkModules: false,
46 + }) + "\n\n"
47 + );
48 + console.log(chalk.greenBright("Build completed with success.\n"));
49 +
50 + compiler.close((closeErr) => {
51 + if (closeErr) {
52 + console.log(
53 + chalk.redBright(
54 + `compiler close failed with message ${closeErr.message}`
55 + )
56 + );
57 + }
58 + });
59 + });
60 +});
1 +module.exports = {
2 + admin: {
3 + dev: {
4 + env: "development",
5 + publicPath: "/",
6 + host: "localhost",
7 + port: "8090",
8 + assetsSubDirectory: "static",
9 + devtoolType: "eval-cheap-module-source-map",
10 + proxyTable: {
11 + "/admin_api": {
12 + target: "http://localhost:3000/admin_api/",
13 + changeOrigin: true,
14 + pathRewrite: {
15 + "^/admin_api": "/",
16 + },
17 + },
18 + },
19 + },
20 + build: {
21 + env: "production",
22 + publicPath: "./", // html引用资源路径
23 + assetsPath: "static", // 静态资源目录
24 + assetsSubDirectory: "static", // html资源存放目录
25 + devtoolType: "source-map", // 代码位置信息
26 + },
27 + },
28 +
29 + client: {
30 + dev: {
31 + env: "development",
32 + publicPath: "/",
33 + host: "localhost",
34 + port: "8080",
35 + assetsSubDirectory: "static",
36 + devtoolType: "eval-cheap-module-source-map",
37 + proxyTable: {
38 + "/client_api": {
39 + target: "http://localhost:3000/client_api/",
40 + changeOrigin: true,
41 + pathRewrite: {
42 + "^/client_api": "/",
43 + },
44 + },
45 + },
46 + },
47 + build: {
48 + env: "production",
49 + publicPath: "./",
50 + assetsPath: "static",
51 + assetsSubDirectory: "static",
52 + devtoolType: "source-map",
53 + },
54 + },
55 +};
1 +"use strict";
2 +const path = require("path");
3 +const VueLoaderPlugin = require("vue-loader/lib/plugin");
4 +// 以link标签形式,抽离出css
5 +const MiniCssExtractPlugin = require("mini-css-extract-plugin");
6 +
7 +const isAdmin = process.env.NODE_ENV_TYPE === "admin";
8 +const isProd = process.env.NODE_ENV === "prod";
9 +const prodConf = isAdmin
10 + ? require("./config").admin.build
11 + : require("./config").client.build;
12 +// 拼接路径
13 +const resolve = (dir) => {
14 + return path.join(__dirname, "..", dir);
15 +};
16 +// 资源路径
17 +const assetsPath = (dir) => {
18 + return path.posix.join(prodConf.assetsPath, dir);
19 +};
20 +
21 +module.exports = {
22 + entry: {
23 + app: [
24 + isAdmin
25 + ? resolve("code/admin/src/main.js")
26 + : resolve("code/client/src/main.js"),
27 + "babel-polyfill",
28 + ],
29 + },
30 + // 配置模块如何被解析
31 + resolve: {
32 + //自动解析文件扩展名(补全文件后缀)(从左->右)
33 + extensions: [".js", ".vue", ".json"],
34 + // 配置别名映射
35 + alias: {
36 + vue$: "vue/dist/vue.esm.js",
37 + src: isAdmin ? resolve("code/admin/src") : resolve("code/client/src"),
38 + components: isAdmin
39 + ? resolve("code/admin/src/components")
40 + : resolve("code/client/src/components"),
41 + style: isAdmin
42 + ? resolve("code/admin/src/styles")
43 + : resolve("code/client/src/style"),
44 + views: isAdmin
45 + ? resolve("code/admin/src/views")
46 + : resolve("code/client/src/views"),
47 + store: isAdmin
48 + ? resolve("code/admin/src/store")
49 + : resolve("code/client/src/store"),
50 + api: isAdmin
51 + ? resolve("code/admin/src/api")
52 + : resolve("code/client/src/api"),
53 + utils: isAdmin
54 + ? resolve("code/admin/src/utils")
55 + : resolve("code/client/src/utils"),
56 + },
57 + },
58 + plugins: [
59 + // 引用vue-loader时必须确保引入该plugin
60 + new VueLoaderPlugin(),
61 + new MiniCssExtractPlugin({
62 + filename: assetsPath("css/[name].[chunkhash].css"),
63 + chunkFilename: assetsPath("css/[name].[chunkhash].css"),
64 + }),
65 + ],
66 + module: {
67 + rules: [
68 + {
69 + test: /\.js$/,
70 + loader: "babel-loader",
71 + include: isAdmin
72 + ? resolve("code/admin/src")
73 + : resolve("code/client/src"),
74 + },
75 + {
76 + test: /\.vue$/,
77 + loader: "vue-loader",
78 + include: isAdmin
79 + ? resolve("code/admin/src")
80 + : resolve("code/client/src"),
81 + },
82 + {
83 + test: /\.css$/,
84 + use: [
85 + !isProd
86 + ? { loader: "vue-style-loader" }
87 + : MiniCssExtractPlugin.loader,
88 + { loader: "css-loader" },
89 + ],
90 + },
91 + // 开发环境使用vue-style-loader可以重载样式模块
92 + {
93 + test: /\.less$/,
94 + use: [
95 + !isProd
96 + ? { loader: "vue-style-loader" }
97 + : MiniCssExtractPlugin.loader,
98 + { loader: "css-loader" },
99 + { loader: "less-loader" },
100 + {
101 + loader: "style-resources-loader",
102 + options: {
103 + patterns: path.resolve(__dirname, "../styles/theme.less"),
104 + },
105 + },
106 + ],
107 + },
108 + {
109 + test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/,
110 + loader: "url-loader",
111 + options: {
112 + limit: 10000,
113 + name: assetsPath("img/[name].[hash:7].[ext]"),
114 + },
115 + },
116 + {
117 + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
118 + loader: "url-loader",
119 + options: {
120 + limit: 10000,
121 + name: assetsPath("fonts/[name].[hash:7].[ext]"),
122 + },
123 + },
124 + ],
125 + },
126 +};
1 +"use strict";
2 +const webpack = require("webpack");
3 +// 合并配置
4 +const { merge } = require("webpack-merge");
5 +// 配置需打包的html入口信息,并创建一个新的html文件
6 +const HtmlWebpackPlugin = require("html-webpack-plugin");
7 +// 编译提示
8 +const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
9 +// 系统桌面通知
10 +const notifier = require("node-notifier");
11 +
12 +const isAdmin = process.env.NODE_ENV_TYPE === "admin";
13 +// 开发环境配置参数
14 +const devConf = isAdmin
15 + ? require("./config").admin.dev
16 + : require("./config").client.dev;
17 +const baseConf = require("./webpack.base.conf");
18 +
19 +const dev = merge(baseConf, {
20 + mode: "development",
21 + output: {
22 + filename: "[name]-[hash].js",
23 + // html引用资源路径,在dev-server中,引用的是内存中文件
24 + publicPath: devConf.publicPath,
25 + },
26 + module: {},
27 + // 生成sourceMap(方便调试)
28 + devtool: devConf.devtoolType,
29 + // 启动一个本地服务器,可进行本地开发
30 + devServer: {
31 + hot: true, // 热加载
32 + open: true, // 自动打开浏览器
33 + historyApiFallback: true, // 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
34 + host: devConf.host,
35 + port: devConf.port,
36 + proxy: devConf.proxyTable, // 配置反向代理解决跨域
37 + compress: true, // 压缩代码
38 + client: {
39 + // 在浏览器上全屏显示编译的errors
40 + overlay: {
41 + errors: true,
42 + warnings: false,
43 + },
44 + },
45 + },
46 + watchOptions: {
47 + ignored: ["**/.#*.vue", "node_modules/**"],
48 + },
49 + plugins: [
50 + // 开启HMR(热替换功能,替换更新部分,不重载页面!)
51 + new webpack.HotModuleReplacementPlugin(),
52 + new HtmlWebpackPlugin({
53 + filename: "index.html",
54 + template: isAdmin ? "code/admin/index.html" : "code/client/index.html",
55 + inject: true,
56 + }),
57 + // 编译提示
58 + new FriendlyErrorsPlugin({
59 + // 编译成功
60 + compilationSuccessInfo: {
61 + messages: [
62 + `Your application is running here: http://${devConf.host}:${devConf.port}`,
63 + ],
64 + },
65 + // 编译出错
66 + onErrors: (severity, errors) => {
67 + if (severity !== "error") {
68 + return;
69 + }
70 + const error = errors[0];
71 + // 编译出错时,系统右下角弹出错误提示
72 + notifier.notify({
73 + title: "Webpack error",
74 + message: severity + ": " + error.name,
75 + subtitle: error.file || "",
76 + });
77 + },
78 + clearConsole: true, // 每次编译之间清除控制台
79 + }),
80 + ],
81 +});
82 +module.exports = dev;
1 +"use strict";
2 +const path = require("path");
3 +const webpack = require("webpack");
4 +// 合并配置
5 +const { merge } = require("webpack-merge");
6 +// 配置需打包的html入口信息,并创建一个新的html文件
7 +const HtmlWebpackPlugin = require("html-webpack-plugin");
8 +// 压缩js
9 +const TerserPlugin = require("terser-webpack-plugin");
10 +// 压缩css
11 +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
12 +// 开启Gzip压缩,nginx需要配置
13 +const CompressionPlugin = require("compression-webpack-plugin");
14 +
15 +const isAdmin = process.env.NODE_ENV_TYPE === "admin";
16 +// 生产环境配置参数
17 +const prodConf = isAdmin
18 + ? require("./config").admin.build
19 + : require("./config").client.build;
20 +const baseConf = require("./webpack.base.conf");
21 +// 资源路径
22 +const assetsPath = (dir) => {
23 + return path.posix.join(prodConf.assetsSubDirectory, dir);
24 +};
25 +
26 +const prod = merge({}, baseConf, {
27 + mode: "production",
28 + output: {
29 + // build后所有文件存放的位置
30 + path: path.resolve(__dirname, `../public/${isAdmin ? "admin" : "client"}`),
31 + // html引用资源路径,可在此配置cdn引用地址
32 + publicPath: prodConf.publicPath,
33 + filename: assetsPath("js/[name].[chunkhash].js"),
34 + // 用于打包require.ensure(代码分割)方法中引入的模块
35 + chunkFilename: assetsPath("js/[name].[chunkhash].js"),
36 + },
37 + module: {},
38 + optimization: {
39 + moduleIds: "deterministic", // 被哈希转化成的小位数值模块名
40 + minimizer: [
41 + new TerserPlugin({
42 + parallel: true,
43 + terserOptions: {
44 + format: {
45 + comments: false, // 删除注释
46 + },
47 + },
48 + }),
49 + new CssMinimizerPlugin({
50 + parallel: true,
51 + minimizerOptions: {
52 + preset: [
53 + "default",
54 + {
55 + discardComments: { removeAll: true }, // 删除注释
56 + },
57 + ],
58 + },
59 + }),
60 + ],
61 + splitChunks: {
62 + chunks: "all",
63 + minSize: 30000, // 形成一个新代码块最小的体积
64 + maxAsyncRequests: 5, // 按需加载时候最大的并行请求数
65 + maxInitialRequests: 3, // 最大初始化请求数
66 + automaticNameDelimiter: "~", // 打包分割符
67 + cacheGroups: {
68 + // 打包第三方库
69 + vendors: {
70 + name: `chunk-vendors`,
71 + test: /[/]node_modules[/]/,
72 + priority: -10, // 优先级
73 + chunks: "initial",
74 + },
75 + // 打包其余的的公共代码
76 + common: {
77 + name: `chunk-common`,
78 + minChunks: 2, // 引入两次及以上被打包
79 + priority: -20,
80 + chunks: "initial",
81 + reuseExistingChunk: true,
82 + },
83 + },
84 + },
85 + },
86 + plugins: [
87 + // 作用域提升,提升代码在浏览器执行速度
88 + new webpack.optimize.ModuleConcatenationPlugin(),
89 + new HtmlWebpackPlugin({
90 + filename: "index.html",
91 + template: isAdmin ? "code/admin/index.html" : "code/client/index.html",
92 + inject: true,
93 + minify: {
94 + removeComments: true, // 删除html注释
95 + collapseWhitespace: true, // 去除空格
96 + removeRedundantAttributes: true, // 删除多余的属性
97 + },
98 + }),
99 + new CompressionPlugin({
100 + filename: "[path][base].gz",
101 + algorithm: "gzip",
102 + test: /\.js$|\.css$|\.html$/,
103 + threshold: 10240,
104 + minRatio: 0.8,
105 + }),
106 + ],
107 +});
108 +// 项目打包后,进行性能分析
109 +if (process.env.analyz_npm_config_report) {
110 + const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
111 + .BundleAnalyzerPlugin;
112 + prod.plugins.push(new BundleAnalyzerPlugin());
113 +}
114 +
115 +module.exports = prod;
1 +{
2 + "name": "rf-blog",
3 + "version": "1.0.0",
4 + "author": "rf",
5 + "description": "A blog website sharing front-end technology",
6 + "keywords": [
7 + "vue",
8 + "node",
9 + "koa",
10 + "mongodb"
11 + ],
12 + "main": "index.js",
13 + "scripts": {
14 + "dev:admin": "cross-env NODE_ENV_TYPE=admin webpack-dev-server --config build/webpack.dev.conf.js",
15 + "dev:client": "cross-env NODE_ENV_TYPE=client webpack-dev-server --config build/webpack.dev.conf.js",
16 + "build:admin": "cross-env NODE_ENV_TYPE=admin NODE_ENV=prod node build/build.js",
17 + "build:client": "cross-env NODE_ENV_TYPE=client NODE_ENV=prod node build/build.js",
18 + "analyz:admin": "cross-env analyz_npm_config_report=true npm run build:admin",
19 + "analyz:client": "cross-env analyz_npm_config_report=true npm run build:client",
20 + "server": "cross-env NODE_ENV=development nodemon code/server/index.js",
21 + "start": "pm2 start code/server/index.js",
22 + "stop": "pm2 stop code/server/index.js",
23 + "restart": "pm2 restart code/server/index.js"
24 + },
25 + "license": "ISC",
26 + "devDependencies": {
27 + "babel-core": "^6.26.0",
28 + "babel-loader": "^7.1.2",
29 + "babel-plugin-transform-runtime": "^6.23.0",
30 + "babel-polyfill": "^6.26.0",
31 + "babel-preset-env": "^1.6.1",
32 + "babel-preset-es2015": "^6.24.1",
33 + "babel-preset-stage-2": "^6.24.1",
34 + "babel-register": "^6.26.0",
35 + "chalk": "^2.3.0",
36 + "compression-webpack-plugin": "^8.0.1",
37 + "cross-env": "^5.1.3",
38 + "css-loader": "^0.28.8",
39 + "css-minimizer-webpack-plugin": "^3.0.2",
40 + "file-loader": "^1.1.6",
41 + "friendly-errors-webpack-plugin": "^1.6.1",
42 + "html-webpack-plugin": "^5.3.2",
43 + "less": "^2.7.3",
44 + "less-loader": "^4.0.5",
45 + "mini-css-extract-plugin": "^2.2.0",
46 + "node-notifier": "^5.1.2",
47 + "nodemon": "^1.12.1",
48 + "ora": "^1.3.0",
49 + "postcss-loader": "^2.0.10",
50 + "rimraf": "^2.6.2",
51 + "style-resources-loader": "^1.4.1",
52 + "terser-webpack-plugin": "^5.1.4",
53 + "url-loader": "^0.6.2",
54 + "vue-loader": "^15.7.0",
55 + "vue-style-loader": "^4.1.3",
56 + "vue-template-compiler": "^2.5.13",
57 + "webpack": "^5.51.1",
58 + "webpack-bundle-analyzer": "^4.4.2",
59 + "webpack-cli": "^4.8.0",
60 + "webpack-dev-server": "^4.0.0",
61 + "webpack-merge": "^5.8.0"
62 + },
63 + "dependencies": {
64 + "axios": "^0.17.0",
65 + "babel-polyfill": "^6.26.0",
66 + "busboy": "^0.2.14",
67 + "clipboard": "^2.0.8",
68 + "element-ui": "^2.0.11",
69 + "highlight.js": "^10.7.0",
70 + "ip": "^1.1.5",
71 + "js-md5": "^0.7.3",
72 + "jsonwebtoken": "^8.1.0",
73 + "koa": "^2.4.1",
74 + "koa-bodyparser": "^4.2.0",
75 + "koa-router": "^7.3.0",
76 + "koa-static": "^4.0.2",
77 + "log4js": "^2.4.1",
78 + "marked": "^0.3.12",
79 + "mongoose": "^4.13.9",
80 + "nprogress": "^0.2.0",
81 + "qs": "^6.5.1",
82 + "vue": "^2.5.13",
83 + "vue-color": "^2.8.1",
84 + "vue-lazyload": "^1.3.3",
85 + "vue-router": "^3.0.1",
86 + "vuex": "^3.0.1"
87 + }
88 +}
1 +module.exports = {
2 + plugins: [
3 + require("autoprefixer")({
4 + browsers: ["iOS >= 7", "Android >= 4.1", 'last 5 versions']
5 + })
6 + ]
7 +};
No preview for this file type
File mode changed
1 +/**
2 + * 变量样式
3 + */
4 +@mainColor: #333; // 主颜色
5 +@thinColor: #555; // 描述色
6 +@assistColor: #999; // 辅助色
7 +@warningColor: #ff6c1a; // 提醒色
8 +@errorColor: #fe3438; //错误、禁用色
9 +@highlightColor: #009688; // 高亮色
10 +@thinHighlightColor: #20b2aa; // 高亮浅色
11 +@borderColor: #ededed; // 边框色
12 +@borderBoldColor: #ccc; // 边框辅色
13 +@cuttingLineColor: #f0f0f0; // 分割线色
14 +@backgroundColor: rgba(233, 234, 237, 0.7); // 背景色
15 +@thinBgColor: #f6f6f6; // 背景色
16 +
17 +@baseSpH: 24px; // 基础左右间距
18 +@baseSp: 20px; // 基础间距
19 +
20 +@min-body-height: calc(~"100vh - 50px"); // 页面最小高度,去除顶部的高度
21 +
22 +.ellipsis-line-clamp (@line: 1) {
23 + display: -webkit-box;
24 + text-overflow: ellipsis;
25 + -webkit-line-clamp: @line;
26 + -webkit-box-orient: vertical;
27 + overflow: hidden;
28 +}
This diff could not be displayed because it is too large.