webpack4.0從零到一搭建vue項(xiàng)目框架 vue + vue-router + vuex + hot + 換膚 + moke...

主要內(nèi)容

一. 初始化工程項(xiàng)目
二.添加熱部署
三.添加vue套餐
四.添加換膚功能
五.添加項(xiàng)目端口提示
六.添加axios并二次封裝
七.添加moke.js模擬后端數(shù)據(jù)
八.設(shè)置環(huán)境變量

一. 初始化工程項(xiàng)目

生成基礎(chǔ)項(xiàng)目

npm init -y 

創(chuàng)建 src辱挥、dist目錄 , src/main.js蝗砾、index.html、webpack.config.js文件
目錄如下

project
├── dist
├── src
│   └── main.js
├── index.html
├── package.json
└── webpack.config.js

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>webpack</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

main.js

var content = document.createElement("div");
content.textContent = "Hello webpack!";
document.querySelector("#app").appendChild(content);

引入webpack webpack-cli html-webpack-plugin三個(gè)包

npm i webpack webpack-cli html-webpack-plugin -D 

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: __dirname + "/src/main.js", // 打包入口
    output: { // 出口文件
        path: __dirname + "/dist", // 打包后存放的地方
        filename: "bundle.js" // 打包后的文件命名
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'webpack',
            minify: { // 壓縮HTML文件
                removeComments: true, // 移除HTML中的注釋
                collapseWhitespace: true, // 刪除空白符與換行符
                minifyCSS: true// 壓縮內(nèi)聯(lián)css
            },
            filename: 'index.html', // 輸出的模板名字
            template: 'index.html' // 模板路徑
        })
    ]
}

package.json scripts下面添加一行命令

"build": "webpack --mode production"

執(zhí)行下面代碼

npm run build

可以看到dist目錄多出了兩個(gè)打包文件洞就,一個(gè)是index.html,一個(gè)是bundle.js
瀏覽器打開(kāi)index.html
可以看到頁(yè)面出現(xiàn)了Hello webpack! OK到這里已經(jīng)完成了項(xiàng)目的簡(jiǎn)單打包了。
教練我看到別人的項(xiàng)目可以實(shí)時(shí)刷新掀淘,我也想學(xué)旬蟋,OK,熱部署安排一下革娄。

二.添加熱部署

安裝熱部署依賴

npm i -D webpack-dev-server

webpack.config.js文件添加devServer的配置

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    entry: __dirname + "/src/main.js", // 打包入口
    output: { // 出口文件
        path: __dirname + "/dist", // 打包后存放的地方
        filename: "bundle.js" // 打包后的文件命名
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
        historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
        inline: true, // 實(shí)時(shí)刷新
        port: 8888
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: 'webpack',
            minify: { // 壓縮HTML文件
                removeComments: true, // 移除HTML中的注釋
                collapseWhitespace: true, // 刪除空白符與換行符
                minifyCSS: true// 壓縮內(nèi)聯(lián)css
            },
            filename: 'index.html', // 輸出的模板名字
            template: 'index.html' // 模板路徑
        })
    ]
}

理所當(dāng)然package.json也是要相對(duì)應(yīng)修改

"dev": "webpack-dev-server --open"

運(yùn)行項(xiàng)目

npm run dev

可以看到webpack運(yùn)行后自動(dòng)打開(kāi)了默認(rèn)瀏覽器并在頁(yè)面上輸出了Hello webpack!
修改下main.js內(nèi)容

var content = document.createElement("div");
content.textContent = "Hello webpack! Webpack牛批咖为!";
document.querySelector("#app").appendChild(content);

保存修改后可以發(fā)現(xiàn)瀏覽器的內(nèi)容也隨著自動(dòng)刷新了,到這里webpack熱加載就完成了稠腊。

三.添加vue套餐

安裝babel,vue套餐依賴躁染,css依賴 @注意這里的babel-loader建議安裝7.x版本 頭鐵的可以不指定版本安裝最新版
-S --save 運(yùn)行時(shí)依賴 dependencies
-D --save-dev 開(kāi)發(fā)時(shí)依賴 devDependencies
此外還有peerDependencies、bundleDependencies架忌、optionalDependencies
前兩項(xiàng)其實(shí)已經(jīng)足夠日常使用了吞彤,后三項(xiàng)是作為npm包的發(fā)布者需要考慮使用的,有興趣可以查閱原文章以及npm的文檔

npm i -S vue vue-router 
npm i -D babel-core babel-loader@7.1.1 vue-loader vue-loader vue-template-compiler css-loader

項(xiàng)目中添加src/views叹放、src/router目錄饰恕, src/views/index.vue src/router/index.js、src/App.vue 文件
目錄如下,帶有+號(hào)的就是新增的

project
├── index.html
├── list.md
├── package.json
├── dist
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.vue       ++++++++++++++++          
│   ├── main.js                     
│   ├── router        ++++++++++++++++  
│   │   └── index.js  ++++++++++++++++          
│   └── views         ++++++++++++++++      
│       └── index.vue ++++++++++++++++      
└── webpack.config.js

views/index.vue

<template>
  <div>vue webpack</div>
</template>

<script>
export default {
  name: 'Home',
  data () {
    return {
    }
  }
}
</script>

<style>
</style>

router/index.js

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
    routes: [
      {
        path: '/',
        name: 'home',
        component: (resolve) => require(['../views/index.vue'], resolve),
      }
    ],
    mode: 'history'
})

App.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {
  },
  data () {
    return {
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  width: 100%;
  min-width: 1200px;
  height: 100%;
  top: 0;
  left: 0;
}
</style>

main.js

import Vue from "vue";
import App from "./App.vue";
import router from './router'

new Vue({
    el: "#app",
    router,
    components: {
        App,
    },
    template: "<App/>"
});

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    entry: __dirname + "/src/main.js", // 打包入口
    output: { // 出口文件
        path: __dirname + "/dist", // 打包后存放的地方
        filename: "bundle.js", // 打包后的文件命名
        publicPath: '/'
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
        historyApiFallback: false, // 找不到界面默認(rèn)返回首頁(yè)
        inline: true, // 實(shí)時(shí)刷新
        port: 8888
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js' //重定向代理
        }
    },
    module: {
        rules: [
            { 
                test: /\.css$/, 
                use:'css-loader', 
                include: path.resolve(__dirname + '/src/'), 
                exclude: /node_modules/
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: /node_modules/
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            title: 'webpack',
            minify: { // 壓縮HTML文件
                removeComments: true, // 移除HTML中的注釋
                collapseWhitespace: true, // 刪除空白符與換行符
                minifyCSS: true// 壓縮內(nèi)聯(lián)css
            },
            filename: 'index.html', // 輸出的模板名字
            template: 'index.html' // 模板路徑
        })
    ]
}

運(yùn)行項(xiàng)目

npm run dev

修改下index.vue里面的內(nèi)容井仰,把vue webpack改成hello webpack


image.png

下面添加vuex

cnpm i -S vuex

src目錄下添加store埋嵌、store/module目錄, 添加store/index.js、store/store俱恶、store/module/main.js
目錄如下

project
├── index.html
├── list.md
├── package.json
├── dist
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.vue
│   ├── main.js
│   ├── router
│   │   └── index.js
│   ├── store            ++++++++++++++++++++++   
│   │   ├── getters.js   ++++++++++++++++++++++           
│   │   ├── index.js     ++++++++++++++++++++++           
│   │   └── modules      ++++++++++++++++++++++       
│   │       └── main.js  ++++++++++++++++++++++           
│   └── views
│       └── index.vue
└── webpack.config.js

src/main.js

import Vue from "vue";
import App from "./App.vue";
import router from './router'
import store from './store'

new Vue({
    el: "#app",
    router,
    store,
    components: {
        App,
    },
    template: "<App/>"
});

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)

const state = {
}

const mutations = {

}

const actions = {

}

//獲取modules下面的文件數(shù)組
const modulesFiles = require.context('./modules', false, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {})

const store = new Vuex.Store({
  modules,
  getters,
  mutations,
  actions,
  state
})
export default store

getters.js

const getters = {
    test: state => state.main.test
}
export default getters

store/modules/main.js

const state = {
    text: 'webpack 牛批雹嗦!'
  }
  
  const mutations = {

  }
  
  const actions = {}
  
  export default {
    namespaced: true,
    state,
    mutations,
    actions
  }

views/index.vue

<template>
  <div>{{text}}</div>
</template>

<script>
import { mapGetters } from "vuex"
export default {
  name: 'Home',
  data () {
    return {
    }
  },
  computed: {
    ...mapGetters(["text"])
  },
  mounted() {
      console.log(this, "11")
  }
}
</script>

<style>
</style>

運(yùn)行項(xiàng)目

npm run dev

頁(yè)面上出現(xiàn)webpack 牛批!就OK了

四.添加換膚功能

添加全局sass變量

npm i -D style-loader node-sass sass-loader sass-resources-loader postcss-loader autoprefixer

src下創(chuàng)建assets合是、assets/css目錄, assets/css下創(chuàng)建index.scss了罪、mixin.scss、reset.css聪全、theme.scss泊藕、common.scss、flex.scss
目錄如下

project
├── index.html
├── package.json
├── dist
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets                 ++++++++++++++++++++ 
│   │   └── css                ++++++++++++++++++++ 
│   │       ├── common.scss    ++++++++++++++++++++             
│   │       ├── index.scss     ++++++++++++++++++++             
│   │       ├── mixin.scss     ++++++++++++++++++++             
│   │       ├── reset.css      ++++++++++++++++++++        
│   │       ├── flex.css       ++++++++++++++++++++             
│   │       └── theme.scss     ++++++++++++++++++++             
│   ├── main.js
│   ├── router
│   │   └── index.js
│   ├── store
│   │   ├── getters.js
│   │   ├── index.js
│   │   └── modules
│   │       └── main.js
│   └── views
│       └── index.vue
└── webpack.config.js

theme.scss

$font-color-theme1: blue;
$bg-theme1: blue;


$font-color-theme2: red;
$bg-theme2: red;

$font-color-theme3: yellow;
$bg-theme3: yellow;

reset.css

/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
    display: block;
}
html {
    height: 100%;
}
body {
    line-height: 1;
    height: 100%;
}
ol, ul {
    list-style: none;
}
blockquote, q {
    quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
}
a{
    text-decoration: none;
    color: #000;
}
a:active{
    text-decoration: none;
    color: #000;
}
.clearfixed{
    clear: both;
}
.clearfixed::after, .clearfixed::before {
    content: "";
    clear: both;
    display: block;
    height: 0;
    width: 0
}

index.scss

@import "./common.scss";
@import "./flex.scss";
@import "./reset.css";

mixin.scss

@mixin font_color {
  color: $primary-theme1;
  [data-theme="theme1"] & {
    color: $primary-theme1;
  }

  [data-theme="theme2"] & {
    color: $primary-theme2;
  }

  [data-theme="theme3"] & {
    color: $primary-theme3;
  }
}

@mixin primary_color {
  color: $primary-theme1;
  [data-theme="theme1"] & {
    color: $primary-theme1;
  }

  [data-theme="theme2"] & {
    color: $primary-theme2;
  }

  [data-theme="theme3"] & {
    color: $primary-theme3;
  }
}

@mixin bg_color {
  background: $primary-theme1;
  [data-theme="theme1"] & {
    background: $primary-theme1;
  }

  [data-theme="theme2"] & {
    background: $primary-theme2;
  }

  [data-theme="theme3"] & {
    background: $primary-theme3;
  }
}

@mixin solid_color {
  border: 1px solid  $primary-theme1;
  [data-theme="theme1"] & {
    border: 1px solid  $primary-theme1;
  }
  
  [data-theme="theme2"] & {
    border: 1px solid  $primary-theme2;
  }
  
  [data-theme="theme3"] & {
    border: 1px solid  $primary-theme3;
  }
}

// 單行省略號(hào)
@mixin Ellipsis{
  overflow: hidden;text-overflow: ellipsis;white-space: nowrap;
}

// 多行省略號(hào)
@mixin EllipsisMore($height, $lineHeight, $num){
  width:100%;
  min-height: 20px;
  max-height: $height;
  line-height: $lineHeight; 
  overflow: hidden; 
  text-overflow: ellipsis; 
  display: -webkit-box; 
  -webkit-line-clamp: $num;
  -webkit-box-orient: vertical;
}

// flex居中

@mixin flexCenter{
  display: flex;
  justify-content: center;
  align-items: center;
}
// flex居中

@mixin flexCenter{
  display: flex;
  justify-content: center;
  align-items: center;
}

common.scss

.clear {
    clear: both;
}

.cursor {
    cursor: pointer;
}

.font12 {
    font-size: 12px;
}

.font14 {
    font-size: 14px;
}

.font16 {
    font-size: 16px;
}

.font18 {
    font-size: 18px;
}

.font20 {
    font-size: 20px;
}

.font24 {
    font-size: 24px;
}

.font30 {
    font-size: 30px;
}

.font36 {
    font-size: 36px;
}

.font46 {
    font-size: 46px;
}

.weight {
    font-weight: bold;
}

.clear {
    clear: bold;
}

.clearfloat:after {
    display: block;
    clear: both;
    content: "";
    visibility: hidden;
    height: 0;
}

.clearfloat {
    zoom: 1;
}

[v-cloak] {
    display: none;
}

.el-select .el-input .el-select__caret {
    -ms-transition: transform .3s;
    -ms-transform: rotateZ(180deg);
}

.font-text {
    font-size: 14px;
    color: #999;
}

.font-title {
    color: #333;
    font-size: 18px;
}

.hot {
    color: #b71ed7;
    font-weight: 500;
    font-style: inherit;
}

.red {
    color: #e8001c;
    font-weight: 500;
    font-style: inherit;
}

.hoverColor1 {
    cursor: pointer;
    &:hover {
        color: #ef5924 !important;
    }
}

.hoverColor2 {
    cursor: pointer;
    &:hover {
        background: #f1f1f1 !important;
    }
}

flex.scss

.flex {
    display: flex;
  }
  
  .flex1 {
    flex: 1;
  }
  
  .flex2 {
    flex: 2;
  }
  
  .flex3 {
    flex: 3;
  }
  
  .flex4 {
    flex: 4;
  }
  
  .jcenter {
    justify-content: center;
  }
  
  .column {
    flex-direction: column;
  }
  
  .row {
    flex-direction: row;
  }
  
  .aitem {
    align-items: center;
  }
  
  .clearbox {
    box-sizing: border-box;
  }
  
  .jaround {
    justify-content: space-around;
  }
  
  .jbetween {
    justify-content: space-between;
  }

webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    entry: __dirname + "/src/main.js", // 打包入口
    output: { // 出口文件
        path: __dirname + "/dist", // 打包后存放的地方
        filename: "bundle.js", // 打包后的文件命名
        publicPath: '/'
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
        historyApiFallback: false, // 找不到界面默認(rèn)返回首頁(yè)
        inline: true, // 實(shí)時(shí)刷新
        port: 8888
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js' , // 重定向代理
+         "@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
        }
    },
    module: {
        rules: [
            { 
                test: /\.css$/, 
                use:'css-loader', 
                include: path.resolve(__dirname + '/src/'), 
                exclude: /node_modules/
            },
+           {
+               test: /\.scss$/,
+               use: [
+                   'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
+                   'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
+                   'sass-loader', // 將 Sass 編譯成 CSS
+                   {
+                       loader: "postcss-loader",//添加瀏覽器兼容后綴
+                       options: {
+                           plugins: [
+                               require("autoprefixer")(),
+                           ]
+                       }
+                   },
+                   {
+                       loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
+                       options: {
+                         resources: [
+                             path.resolve(__dirname + "/src/assets/css/theme.scss"), 
+                             path.resolve(__dirname + "/src/assets/css/mixin.scss")
+                           ] 
+                       },
+                   },
+               ]
+           },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: /node_modules/
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
            title: 'webpack',
            minify: { // 壓縮HTML文件
                removeComments: true, // 移除HTML中的注釋
                collapseWhitespace: true, // 刪除空白符與換行符
                minifyCSS: true// 壓縮內(nèi)聯(lián)css
            },
            filename: 'index.html', // 輸出的模板名字
            template: 'index.html' // 模板路徑
        })
    ]
}

到這里基本是完成了难礼,不過(guò)是scss全局變量完成娃圆,實(shí)行換膚還差一點(diǎn)點(diǎn)
src下新建utils文件夾,新建utils/index.js文件
目錄如下

project
├── index.html
├── package.json
├── dist
│   ├── bundle.js
│   └── index.html
├── src
│   ├── App.vue
│   ├── assets                 
│   │   └── css                
│   │       ├── common.scss    
│   │       ├── index.scss     
│   │       ├── mixin.scss     
│   │       ├── reset.css      
│   │       └── theme.scss     
│   ├── main.js
│   ├── router
│   │   └── index.js
│   ├── store
│   │   ├── getters.js
│   │   ├── index.js
│   │   └── modules
│   │       └── main.js
│   ├── utils               ++++++++++
│   │   └── index.js        ++++++++++
│   └── views
│       └── index.vue
└── webpack.config.js

src/main.js修改

import Vue from "vue";
import App from "./App.vue";
import router from './router'
import store from './store'
import "@/assets/css/index.scss";

if(localStorage.getItem("WINDOWS_THEME")) {
    window.document.documentElement.setAttribute('data-theme', localStorage.getItem("WINDOWS_THEME"))
}

new Vue({
    el: "#app",
    router,
    store,
    components: {
        App,
    },
    template: "<App/>"
});

views/index.vue修改

<template>
  <div class="index-wrapper">
    <header class="index-header flex">
      <div class="theme theme1"></div>
      <div class="theme theme2"></div>
      <div class="theme theme3"></div>
    </header>
    <div class="content">
      <div class="primary jcenter aitem flex">主題色</div>
      <div class="bth-box">
        <div class="list cursor theme1" @click="changeTheme('theme1')">改變主題色1</div>
        <div class="list cursor theme2" @click="changeTheme('theme2')">改變主題色2</div>
        <div class="list cursor theme3" @click="changeTheme('theme3')">改變主題色3</div>
      </div>
    </div>
  </div>
</template>

<script>
import { mapGetters } from "vuex"
import { setTheme } from "@/utils"
export default {
  name: 'Home',
  data () {
    return {
    }
  },
  computed: {
    ...mapGetters(["text"])
  },
  methods: {
    changeTheme(type) {
      setTheme(type)
    }
  },
  mounted() {
      console.log(this, "11")
  }
}
</script>

<style  lang="scss" scoped>
.index-wrapper {
  .theme {
    width: 100px;
    height: 100px;
  }
  .theme1 {
    background: $primary-theme1;
  }
  .theme2 {
    background: $primary-theme2;
  }
  .theme3 {
    background: $primary-theme3;
  }
  .content {
    margin: 50px;
    display: flex;
    .primary {
      width: 100px;
      height: 100px;
      @include bg_color;
      color: #fff;
    }
    .bth-box {
      margin-left: 20px;
      .list {
        width: 100px;
        height: 30px;
        line-height: 30px;
        border: 1px solid #ddd;
        border-radius: 2px;
        margin-bottom: 10px;
        text-align: center;
        color: #fff;
        &.theme1 {
          background: blue;
        }
        &.theme2 {
          background: red;
        }
        &.theme3 {
          background: yellow;
        }
      }
    }
  }
}
</style>

現(xiàn)在頁(yè)面是這樣的


image.png

點(diǎn)擊改變主題色按鈕可以切換了蛾茉,換膚功能完成讼呢。

五.添加項(xiàng)目端口提示

現(xiàn)在項(xiàng)目運(yùn)行完成是這樣的


image.png

別人的項(xiàng)目是這樣的


image.png

好整潔,教練我想學(xué)臀稚,OK
添加控制臺(tái)提示依賴包吝岭,順便把靜態(tài)資源打包插件安裝上三痰,還有文件清除插件
npm i -D friendly-errors-webpack-plugin copy-webpack-plugin clean-webpack-plugin

新建config吧寺、static文件夾窜管,config/index.js,statuc/test.txt文件
目錄如下

project
├── index.html
├── package.json
├── dist
│   ├── bundle.js
│   └── index.html
├── config          +++++++
│   └── index.js    +++++++      
├── static          +++++++
│   └── test.txt    +++++++        
...

config/index.js

module.exports = {
    devServer: {
        contentBase: "../dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
        publicPath: '/', // 公共路徑 打包后資源可以訪問(wèn)的路徑
        historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
        inline: true, //實(shí)時(shí)刷新
        host: '0.0.0.0',
        port: 8888,
        open: false
    },
    onErrors: ()=> {
        const notifier = require('node-notifier')
        return (severity, errors) => {
          if (severity !== 'error') return
          const error = errors[0]
          const filename = error.file && error.file.split('!').pop()
          notifier.notify({
            title: packageConfig.name,
            message: severity + ': ' + error.name,
            subtitle: filename || '',
            icon: path.join(__dirname, 'logo.png')
          })
        }
    }
}

webpack.config.js修改(注意) // copy-webpack-plugin v5.12以上的版本要添加patterns

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const copyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = require('./config')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
    entry: __dirname + "/src/main.js", // 打包入口
    output: { // 出口文件
        path: __dirname + "/dist", // 打包后存放的地方
        filename: "bundle.js", // 打包后的文件命名
        publicPath: '/'
    },
    devServer: config.devServer,
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js', // 重定向代理
           "@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
        }
    },
    module: {
        rules: [
            { 
                test: /\.css$/, 
                use:'css-loader', 
                include: path.resolve(__dirname + '/src/'), 
                exclude: /node_modules/
            },
            {
                test: /\.scss$/,
                use: [
                    'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
                    'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
                    'sass-loader', // 將 Sass 編譯成 CSS
                    {
                        loader: "postcss-loader",//添加瀏覽器兼容后綴
                        options: {
                            plugins: [
                                require("autoprefixer")(),
                            ]
                        }
                    },
                    {
                        loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
                        options: {
                          resources: [
                              path.resolve(__dirname + "/src/assets/css/theme.scss"), 
                              path.resolve(__dirname + "/src/assets/css/mixin.scss")
                            ] 
                        },
                    },
                ]
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: /node_modules/
            }
        ]
    },
    plugins: [
        // v5.12以上的版本要添加patterns
        new copyWebpackPlugin({
            patterns: [
                {
                    from:path.resolve(__dirname+'/static'),// 打包的靜態(tài)資源目錄地址 不經(jīng)過(guò) webpack,需要通過(guò)絕對(duì)路徑來(lái)引用
                    to:'static' // 打包到dist下面的static
                }
            ]
        }),
        new VueLoaderPlugin(),
        new CleanWebpackPlugin(), // 每次打包都先刪除打包目錄
        new FriendlyErrorsWebpackPlugin({
            compilationSuccessInfo: {
                messages: [`Your application is running here: http://${config.devServer.host}:${config.devServer.port}`],
            },
            onErrors: true ? config.devServer.onErrors: undefined // 是否開(kāi)啟錯(cuò)誤捕獲
        }),
        new HtmlWebpackPlugin({
            title: 'webpack',
            minify: { // 壓縮HTML文件
                removeComments: true, // 移除HTML中的注釋
                collapseWhitespace: true, // 刪除空白符與換行符
                minifyCSS: true// 壓縮內(nèi)聯(lián)css
            },
            filename: 'index.html', // 輸出的模板名字
            template: 'index.html' // 模板路徑
        })
    ],
    stats:"none"
}

啟動(dòng)項(xiàng)目

npm run dev
image.png

灰常好稚机,到這里添加控制臺(tái)提示就結(jié)束了幕帆,當(dāng)然FriendlyErrorsWebpackPlugin不僅僅只有啟動(dòng)項(xiàng)目的提示信息,還能識(shí)別某些類別的webpack錯(cuò)誤赖条,并清理失乾,聚合和優(yōu)先級(jí),以提供更好的開(kāi)發(fā)人員體驗(yàn)纬乍。

六.添加請(qǐng)求方法axios并二次封裝

安裝axios

npm i -S axios

src下添加添加api碱茁、data文件夾,添加api/index.js仿贬、api/axiosExpand.js纽竣、data/index.js、test.js目錄如下

project
├── index.html
├── package.json
├── src
│   ├── App.vue
│   ├── api                  ++++++++       
│   │   ├── index.js         ++++++++           
│   │   └── axiosExpand.js   ++++++++               
│   ├── data                 ++++++++       
│   │   ├── index.js         ++++++++           
│   │   └── test.js          ++++++++       
...

api/index.js

/* eslint-disable */
/**
 * @author jie
 *  date 2019/3/26
 *  data 文件夾下面放不同模塊的接口
 *  method: 'get' 'delete' 'post' 'put'
 *  value: true   接口有value時(shí) 參數(shù)以鍵值對(duì)的值拼接到鏈接后面 xxx.com/getUser?name=哈哈&id=2
 *  linkId: true  接口有l(wèi)inkId時(shí) id帶在鏈接后面 類似 xxx.com/getUser/10
 *  random: true  接口有random時(shí) 添加隨機(jī)數(shù)防止接口緩存
 *  headerToken 是否使用token 目前所有接口都默認(rèn)使用
 *  bodyParam: true  請(qǐng)求如果需要一部分參數(shù)帶頭部,一部分參數(shù)body需要在請(qǐng)求的時(shí)候把參數(shù)獨(dú)立放到參數(shù)里面處理
 *
 */
import Axios from 'axios'
import apiData from '../data'
import qs from 'querystring'
import {
  catchAjaxResolve,
  catchAjaxReject
} from './axiosExpand'

// 設(shè)置默認(rèn)值
Axios.defaults.timeout = 30000;
Axios.defaults.baseURL = "/";
Axios.defaults.headers = {
  'Content-Type': 'application/json'
};

// 數(shù)據(jù)請(qǐng)求之前
Axios.interceptors.request.use((config) => {
  return config
}, error => {
  promise.reject(error)
})

// 提交成功并返回?cái)?shù)據(jù)時(shí)
Axios.interceptors.response.use(response => {
    return catchAjaxResolve(response)
  },
  error => {
    return catchAjaxReject(error)
  }
)

function formatAxios(url, data, params, requestType) {
  if (params.linkId) {
    url += '/' + data.id
    data = null
  }
  if (params.random) {
    if (Object.keys(data)) {
      url += '&time=' + Math.random()
    } else {
      url += '?time=' + Math.random()
    }
  }
  if (params.value || requestType === 'get' || requestType === 'delete') {
    url += '?' + qs.stringify(data)
    data = null
  }
  if (data && data.bodyParam) {
    const bodyParam = JSON.parse(JSON.stringify(data.bodyParam));
    delete data.bodyParam
    url += '?' + qs.stringify(data)
    data = bodyParam
  }
  return Axios[requestType](url, data)
}

function Http(name, data = {}) {
  if (Boolean(apiData[name].headers)) {
    for (let keys in apiData[name].headers) {
      if (apiData[name].headers.hasownproperty(keys)) {
        Axios.defaults.headers[keys] = apiData[name].headers[keys]
      }
    }
  }
  return formatAxios(apiData[name].url, data, apiData[name], apiData[name].method)
}

export default Http

api/axiosExpand.js

// 這里用的是elementUI 彈框提示
// import { Message } from 'element-ui'
// 處理ajax頻繁拋出錯(cuò)誤 3秒內(nèi)只顯示一個(gè)錯(cuò)誤信息
export function sendOut(msg) {
    if (!localStorage.ajax_oldTime) {
        localStorage.ajax_oldTime = new Date()
        // Message.error(msg)
    }
    if (((new Date() - new Date(localStorage.ajax_oldTime)) / 1000) > 3) {
        // Message.error(msg)
        localStorage.ajax_oldTime = new Date()
    }
}


// 處理ajax請(qǐng)求成功異常
export function catchAjaxResolve(response) {
    switch (response.data.code) {
        case 200:
            // IE9時(shí)response.data是undefined茧泪,因此需要使用response.request.responseText(Stringify后的字符串)
            let data = null;
            if (response.data == undefined) {
                data = response.request.responseText
            } else {
                data = response.data
            }
            return Promise.resolve(data)
        case 401:
            // 彈出錯(cuò)誤信息 看情況處理
            return false
        case 500:
            // 彈出錯(cuò)誤信息 看情況處理
            return false
        default:
            return false
    }
}
// 處理ajax請(qǐng)求錯(cuò)誤
export function catchAjaxReject(error) {
    switch (error.response.status) {
        case 401:
            sendOut('登陸失效')
            break
        case 404:
            sendOut('網(wǎng)絡(luò)請(qǐng)求不存在')
            break
        case 504:
            sendOut('網(wǎng)頁(yè)請(qǐng)求超時(shí)')
            break
            // 其他錯(cuò)誤蜓氨,直接拋出錯(cuò)誤提示
        default:
            sendOut('網(wǎng)絡(luò)異常')
    }
    return Promise.reject(error.response)
}

main.js添加

import Axios from '@/api'
Vue.prototype.$Axios = Axios

data/index

/**
 * @description ajax接口聚合
 * @author jie
 * @date 2019/8/16
 * @returns VOID
 * @version 1.0
 */
const files = require.context('./', false, /[^index.js]*\.js$/)
let modules = {}
files.keys().forEach((key) => {
    modules = {...modules, ...files(key).default || files(key)}
})
export default modules;

data/test.js, 這里推薦以模塊分類命名,然后里面的接口xxx模塊名_xxx功能

/**
 * @description 測(cè)試模塊相關(guān)接口
 * @author jie
 * @date 2019/8/16
 * @returns VOID
 * @version 1.0
 */
export default {
    // 添加
    test_add: {
        url: '/api/save',
        interFaceType: 'api',
        method: 'post',
        bodyParam: true
    },
    // 刪除
    test_delete: {
        url: '/api/delete',
        interFaceType: 'api',
        method: 'delete'
    },
    // 查詢
    test_getList: {
        url: '/api/select',
        interFaceType: 'api',
        method: 'get',
        linkId: true
    },
    // 修改
    test_updateList: {
        url: '/api/update',
        interFaceType: 'api',
        method: 'put'
    },
}

七.添加moke.js模擬后端數(shù)據(jù)

添加依賴

npm install -D mockjs

添加mock文件夾队伟,mock/index.js穴吹、mock/test.js文件目錄如下

project
├── mock            +++++++++
│   ├── index.js    +++++++++        
│   └── test.js     +++++++++    
...

mack/index.js
@注 Mock.mock( rurl, rtype, template ) rurl可以是字符串或者正則 為了避免get請(qǐng)求帶有參數(shù)的情況下匹配失效盡量使用正則來(lái)匹配
傳送門(mén):MockJS

/**
 * @description moke接口聚合
 * @author jie
 * @date 2019/8/16
 * @returns VOID
 * @version 1.0
 */
// 引入mockjs
const Mock = require('mockjs');

const files = require.context('./', false, /[^index.js]*\.js$/)
let modules = {}
files.keys().forEach((key) => {
    modules = {...modules, ...files(key).default || files(key)}
})
for(let key in modules) {
    if(modules.hasOwnProperty(key)) {
        let obj = modules[key]
        obj.isMock && Mock.mock(new RegExp(obj.url + ".*"), obj.method, obj.fun); // Mock.mock( rurl, rtype, template ) rurl可以是字符串或者正則 為了避免get請(qǐng)求帶有參數(shù)的情況下匹配失效盡量使用正則來(lái)匹配
}

export default modules;

mock/test.js, url: 需要模擬的接口路徑,method: 對(duì)應(yīng)的請(qǐng)求方法嗜侮,fun模擬數(shù)據(jù)的函數(shù)港令,詳細(xì)的可以去看mock的文檔或者相關(guān)教程
isMock 一鍵開(kāi)關(guān),是否使用mock全在你一念之間

/**
 * @description mock測(cè)試模塊相關(guān)接口
 * @author jie
 * @date 2019/8/16
 * @param { isMock } 是否啟用mock數(shù)據(jù)
 * @returns VOID
 * @version 1.0
 */
// 引入mockjs
const Mock = require('mockjs');
// 獲取 mock.Random 對(duì)象
const Random = Mock.Random;
export default {
    // 查詢
    test_getList: {
        url: '/api/select',
        method: 'get',
        isMock: true,
        fun: function() {
            let testData = []
            for (let i = 0; i < 100; i++) {
                let newArticleObject = {
                    title: Random.csentence(5, 30), //  隨機(jī)生成5-30的中文句子
                    name: Random.cname(), // 隨機(jī)生成一個(gè)常見(jiàn)的中文姓名
                    date: Random.date()  // 指示生成的日期字符串的格式,默認(rèn)為yyyy-MM-dd
                }
                testData.push(newArticleObject)
            }
            console.log(testData)
            return {
                code: 200,
                data: testData
            };
        }
    },
    // 添加
    test_add: {
        url: '/api/save',
        method: 'get',
        isMock: false,
        fun: function() {
            let testData = []
            for (let i = 0; i < 100; i++) {
                let newArticleObject = {
                    title: Random.csentence(5, 30), //  隨機(jī)生成5-30的中文句子
                    name: Random.cname(), // 隨機(jī)生成一個(gè)常見(jiàn)的中文姓名
                    date: Random.date()  // 指示生成的日期字符串的格式,默認(rèn)為yyyy-MM-dd
                }
                testData.push(newArticleObject)
            }
            console.log(testData)
            return {
                code: 200,
                data: testData
            };
        }
    },
}

src/main.js

// 引入mockjs
require('./mock')

views/index

  methods: {
    changeTheme(type) {
      setTheme(type)
    },
    async addList() {
      let json = {
        id: 10
      }
      const res = await this.$Axios("test_add", json)
      if(res) {
        console.log(res, "11")
      }
    },
    async deleteList() {
      let json = {
        name: "vue",
        id: 10
      }
      const res = await this.$Axios("test_delete", json)
      if(res) {
        console.log(res)
      }
    },
    async getList() {
      let json = {
        name: "vue",
        id: 10
      }
      const res = await this.$Axios("test_getList", json)
      if(res) {
        console.log(res, "111")
      }
    },
    async updateList() {
      let json = {
        name: "vue",
        id: 10
      }
      const res = await this.$Axios("test_updateList", json)
      if(res) {
        console.log(res)
      }
    },
  },
  mounted() {
      this.addList()
      this.deleteList()
      this.getList()
      this.updateList()
  }

控制臺(tái)打印


image.png

到這里mock數(shù)據(jù)就完成了

八.設(shè)置環(huán)境變量

安裝依賴

npm i -S dotenv

根目錄添加環(huán)境變量.env.development锈颗,.env.beta缠借,.env.production文件

.env.development

ENV = 'development'
INTERFACE_NAME = 'test.com1'

.env.beta

INTERFACE_NAME = 'test.com2'

.env.production

INTERFACE_PATH = 'test.com3'

config/index.js修改

const fs = require('fs')
// 引入環(huán)境變量依賴
const dotenv = require('dotenv')

module.exports = {
  mode: "development",
  devServer: {
    contentBase: "./dist", // 本地服務(wù)器所加載的頁(yè)面所在的目錄
    publicPath: '/', // 公共路徑 打包后資源可以訪問(wèn)的路徑
    historyApiFallback: true, // 找不到界面默認(rèn)返回首頁(yè)
    disableHostCheck: true, // 檢查主機(jī)host 開(kāi)發(fā)環(huán)境下可建議禁止
    inline: true, //實(shí)時(shí)刷新
    host: 'localhost',
    port: 8888,
    open: false
  },
  hiddleEnv: (env) => {
    let configEnv = dotenv.parse(fs.readFileSync(`.env.${env|| 'development'}`))
    for (let k in configEnv) {
        if(configEnv.hasOwnProperty(k)) {
            process.env[k] = configEnv[k]
        }
    }
  },
  onErrors: () => {
    const notifier = require('node-notifier')
    return (severity, errors) => {
      if (severity !== 'error') return
      const error = errors[0]
      const filename = error.file && error.file.split('!').pop()
      notifier.notify({
        title: packageConfig.name,
        message: severity + ': ' + error.name,
        subtitle: filename || '',
        icon: path.join(__dirname, 'logo.png')
      })
    }
  }
}

webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const copyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// webpack4以上要添加VueLoaderPlugin
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = require('./config')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = (env, argv) => {
    argv.env && (config.mode = "production")
    config.hiddleEnv(argv.env)
    return webpackConfigFun()
}

const webpackConfigFun = ()=> {
    return {
        mode: process.env.ENV === 'development' ? 'development': 'production',
        entry: __dirname + "/src/main.js", // 打包入口
        output: { // 出口文件
            path: __dirname + "/dist", // 打包后存放的地方
            filename: "bundle.js", // 打包后的文件命名
            publicPath: '/'
        },
        devServer: config.devServer,
        resolve: {
            alias: {
                'vue$': 'vue/dist/vue.esm.js', // 重定向代理
                "@": path.resolve(__dirname + "/src/") // 設(shè)置模塊基路徑
            }
        },
        module: {
            rules: [
                { 
                    test: /\.css$/, 
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.scss$/,
                    use: [
                        'style-loader', // 將 JS 字符串生成為 style 節(jié)點(diǎn)
                        'css-loader', // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
                        'sass-loader', // 將 Sass 編譯成 CSS
                        {
                            loader: "postcss-loader",//添加瀏覽器兼容后綴
                            options: {
                                plugins: [
                                    require("autoprefixer")(),
                                ]
                            }
                        },
                        {
                            loader: 'sass-resources-loader', // 絕對(duì)路徑引入主題色文件
                            options: {
                                resources: [
                                    path.resolve(__dirname + "/src/assets/css/theme.scss"), 
                                    path.resolve(__dirname + "/src/assets/css/mixin.scss")
                                ] 
                            },
                        },
                    ]
                },
                {
                    test: /\.vue$/,
                    loader: 'vue-loader'
                },
                {
                    test: /\.js$/,
                    loader: 'babel-loader',
                    include: /node_modules/
                }
            ]
        },
        plugins: [
            // v5.12以上的版本要添加patterns
            new copyWebpackPlugin({
                patterns: [
                    {
                        from:path.resolve(__dirname+'/static'),// 打包的靜態(tài)資源目錄地址 不經(jīng)過(guò) webpack,需要通過(guò)絕對(duì)路徑來(lái)引用
                        to:'static' // 打包到dist下面的static
                    }
                ]
            }),
            new VueLoaderPlugin(),
            new CleanWebpackPlugin(), // 每次打包都先刪除打包目錄
            new FriendlyErrorsWebpackPlugin({
                compilationSuccessInfo: {
                    messages: [`Your application is running here: http://${config.devServer.host}:${config.devServer.port}`],
                },
                onErrors: process.env.ENV === 'development' ? config.devServer.onErrors: undefined // 是否開(kāi)啟錯(cuò)誤捕獲
            }),
            new HtmlWebpackPlugin({
                title: 'webpack',
                minify: { // 壓縮HTML文件
                    removeComments: true, // 移除HTML中的注釋
                    collapseWhitespace: true, // 刪除空白符與換行符
                    minifyCSS: true// 壓縮內(nèi)聯(lián)css
                },
                filename: 'index.html', // 輸出的模板名字
                template: 'index.html' // 模板路徑
            })
        ],
        stats: process.env.ENV === 'development' ? 'none': true
    }
}

package.json

  "scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack --env production",
    "build:beta": "webpack --env  beta"
  }

默認(rèn)加載.env.development宜猜,如果需要加載其他則需要--env xxx 對(duì)應(yīng)環(huán)境變量.env.xxx
mode有三個(gè)模式泼返,減少了webpack大量的配置


image.png

Mode: development


image.png

Mode: production
image.png

Mode: none 這個(gè)基本很少用
image.png

環(huán)境變量的核心是利用webpack更改mode變量的行為

var config = {
  entry: './app.js'
  //...
};

module.exports = (env, argv) => {

  if (argv.mode === 'development') {
    config.devtool = 'source-map';
  }

  if (argv.mode === 'production') {
    //...
  }

  return config;
};

webpack文檔地址:webpack配置
到這里環(huán)境變量就初步配置完成了,后面還有一些webpack優(yōu)化姨拥,兼容绅喉,緩存的東西
未完待續(xù)。叫乌。柴罐。這個(gè)項(xiàng)目會(huì)一直更新直到功能完善
項(xiàng)目源碼:vue-webpack4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末娜氏,一起剝皮案震驚了整個(gè)濱河市芳来,隨后出現(xiàn)的幾起案子样悟,更是在濱河造成了極大的恐慌,老刑警劉巖徙菠,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蝶棋,居然都是意外死亡赐稽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)党瓮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)详炬,“玉大人,你說(shuō)我怎么就攤上這事寞奸∏好眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵枪萄,是天一觀的道長(zhǎng)隐岛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)瓷翻,這世上最難降的妖魔是什么礼仗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮逻悠,結(jié)果婚禮上元践,老公的妹妹穿的比我還像新娘。我一直安慰自己童谒,他們只是感情好单旁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著饥伊,像睡著了一般象浑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琅豆,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天愉豺,我揣著相機(jī)與錄音,去河邊找鬼茫因。 笑死蚪拦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冻押。 我是一名探鬼主播驰贷,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洛巢!你這毒婦竟也來(lái)了括袒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤稿茉,失蹤者是張志新(化名)和其女友劉穎锹锰,沒(méi)想到半個(gè)月后芥炭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恃慧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年园蝠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糕伐。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砰琢,死狀恐怖蘸嘶,靈堂內(nèi)的尸體忽然破棺而出良瞧,到底是詐尸還是另有隱情,我是刑警寧澤训唱,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布褥蚯,位于F島的核電站,受9級(jí)特大地震影響况增,放射性物質(zhì)發(fā)生泄漏赞庶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一澳骤、第九天 我趴在偏房一處隱蔽的房頂上張望歧强。 院中可真熱鬧,春花似錦为肮、人聲如沸摊册。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茅特。三九已至,卻和暖如春棋枕,著一層夾襖步出監(jiān)牢的瞬間白修,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工重斑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兵睛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓窥浪,卻偏偏與公主長(zhǎng)得像卤恳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寒矿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344