vite+vue3.ts多頁面項(xiàng)目構(gòu)建

背景

??公司前端項(xiàng)目,是由Vite+Vue3+ts搭建的單頁面項(xiàng)目粘咖,但是隨著需求增多瓮下,發(fā)現(xiàn)越來越多的頁面互相之前沒有關(guān)聯(lián)關(guān)系钝域,耦合度極低例证,項(xiàng)目逐漸變大后會導(dǎo)致每個獨(dú)立頁面的啟動速度慢,打包時間長胀葱,而且每次發(fā)布打包都會影響線上所有頁面(雖然可能代碼沒改動,但是引用的三方npm包庆锦、公共組件等可能會變化)轧葛,這些都有可能導(dǎo)致線上其他頁面被修改尿扯,測試力度不夠的話都可能會導(dǎo)致各類的隱藏bug衷笋。

??由此產(chǎn)生了搭建多頁面項(xiàng)目的想法。

??項(xiàng)目git地址

技術(shù)棧

vite+vue3+ts+eslint+prettier

需求

  1. 腳本自動創(chuàng)建新頁面蚜锨,包括app.vue、index.html晨抡、main.ts则剃、views文件夾棍现;
  2. 單獨(dú)調(diào)試(dev)和打包(build)某個頁面己肮;
  3. 同時調(diào)試(dev)和打包(build)所有頁面;

開始娄柳!

一赤拒、目錄結(jié)構(gòu)

├── README.md 
├── dist               //打包輸出目錄
├── node_modules       //三方
├── public             //公共靜態(tài)資源
├── scripts            //腳本(打包这敬、創(chuàng)建新頁面)
│   ├── template       //創(chuàng)建子頁面的模版
│   ├── newPage.mjs      //創(chuàng)建子頁面的腳本
│   └── build.cjs      //打包所有頁面的腳本
├── src 
│   ├── arrets         //公共靜態(tài)資源
│   ├── components     //公共組件
│   ├── imgs           //圖片
│   ├── utils          //公共方法
│   ├── services       //公共請求
│   └── pages          //多頁面文件夾
├──  pages.json        //子頁面描述說明集合文件
├── .env.development   //開發(fā)-環(huán)境變量
├── .env.prerelease    //預(yù)發(fā)-環(huán)境變量
├── .env.test          //測試-環(huán)境變量
├── .env.production    //生產(chǎn)-環(huán)境變量
├── .eslintrc.cjs      //eslint 配置
├── .gitignore         //git 提交忽略文件
├── .prettierignore    //prettier 忽略文件
├── .prettierrc.json     //prettier 配置
├── tsconfig.json      //ts 配置
├── vite.config.ts     //vite 配置
├── package.json
├── package-lock.json

二鹅颊、新建項(xiàng)目

vite創(chuàng)建vue項(xiàng)目堪伍,創(chuàng)建一個基礎(chǔ)模板就行觅闽,選擇ts蛉拙,其他router,store吮廉,sass等等隨意宦芦,不做贅述轴脐。

npm 安裝 prettier大咱、eslint、chalk(可以給打印臺的文字加顏色)
不安裝的話其中有些腳本可能會報錯溯捆。
這幾個包涉及到的邏輯不影響功能厦瓢,如果看得懂的話可以將對應(yīng)的腳本修改碳锈、優(yōu)化或刪除售碳,主要就是格式化文本,打印臺輸出文字變色提醒等

配置vite.config.ts

路徑別名间景、配置靜態(tài)資源目錄艺智、配置代理解決測試地址跨域問題等等

詳細(xì)介紹不再這里贅述倘要,自行查閱。

// vite.config.ts
...
resolve: {
    alias: {
      '@': path.join(__dirname, './src'),
      '@pages': path.join(__dirname, './src/pages')
    }
}
...
base: './'  // 靜態(tài)資源基礎(chǔ)路徑
...
server: {
    host: 'localhost', // 指定主機(jī)名
    port: 8080, // 指定端口
    hmr: true,  // 開啟熱更新
    open: true, // 在服務(wù)器啟動時自動在瀏覽器中打開應(yīng)用程序
    proxy: { // 代理解決跨域問題
      '/request': {
        target: 'http://localhost:8081/',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/request/, '')
      }
    }
}
...
配置tsconfig.json

配置可以酌情使用十拣,其中重要的是include封拧,需要包含scripts下的文件,否則會有些報錯夭问。

  // tsconfig.json
  {
    "extends": "@vue/tsconfig/tsconfig.dom.json",
    "compilerOptions": {
      "composite": true,
      // "target": "esnext", //用于指定 TS 最后編譯出來的 ES 版本
      "types": ["vite/client", "node"], //要包含的類型聲明文件名列表
      "useDefineForClassFields": true, //將 class 聲明中的字段語義從 [[Set]] 變更到 [[Define]]
      "module": "esnext", // 設(shè)置編譯后代碼使用的模塊化系統(tǒng):commonjs | UMD | AMD | ES2020 | ESNext | System
      "moduleResolution": "node", // 模塊解析策略泽西,ts默認(rèn)用node的解析策略,即相對的方式導(dǎo)入
      "strict": true, //開啟所有的嚴(yán)格檢查
      "jsx": "preserve", //在 `.tsx`文件里支持JSX: `"React"`或 `"Preserve"`
      "sourceMap": false, // 生成目標(biāo)文件的sourceMap文件
      "resolveJsonModule": true, //允許導(dǎo)入擴(kuò)展名為“.json”的模塊
      "isolatedModules": true, //確保每個文件都可以在不依賴其他導(dǎo)入的情況下安全地進(jìn)行傳輸
      "esModuleInterop": true, //支持導(dǎo)入 CommonJs 模塊
      "lib": ["esnext", "dom", "ES2015"], //TS需要引用的庫缰趋,即聲明文件捧杉,es5 默認(rèn)引用dom、es5秘血、scripthost,如需要使用es的高級版本特性灰粮,通常都需要配置,如es8的數(shù)組新特性需要引入"ES2019.Array",
      // "noLib": false, //不包含默認(rèn)的庫文件( lib.d.ts)
      "skipLibCheck": true, //忽略所有的聲明文件( *.d.ts)的類型檢查
      "allowJs": true, // 允許編譯器編譯JS韧骗,JSX文件
      "noEmit": true, // 不輸出文件,即編譯后不會生成任何js文件
      "allowImportingTsExtensions": true,
      "allowSyntheticDefaultImports": true, //允許從沒有設(shè)置默認(rèn)導(dǎo)出的模塊中默認(rèn)導(dǎo)入些侍。這并不影響代碼的輸出淋样,僅為了類型檢查。默認(rèn)值:module === "system" 或設(shè)置了 --esModuleInterop 且 module 不為 es2015 / esnext
      "baseUrl": "./", //// 解析非相對模塊的基地址余指,默認(rèn)是當(dāng)前目錄
      "paths": {
        "@/*": ["src/*"], //解決引入報錯  找不到模塊“@/xxxx” 或其相應(yīng)的類型聲明
        "@pages/*": ["src/pages/*"]
      }
    },
    "include": [
      "src/**/*.ts",
      "src/**/*.js",
      "src/**/*.d.ts",
      "src/**/*.tsx",
      "src/**/*.vue",
      "src/**/*.json",
      "src/env.d.ts",
      "src/global.d.ts",
      "scripts/**/*.ts",
      "scripts/**/*.vue"
    ],
    "exclude": ["vite.config.ts", "src/**/__tests__/*"]
  }

三淮韭、實(shí)現(xiàn)需求1粱腻,腳本創(chuàng)建新頁面

命令

npm run new-page

腳本使用的是node.js,主要使用的是fs模塊,操作文件夾及文件。其中各種方法的含義自行查詢

1. 將scripts文件夾復(fù)制到自己的項(xiàng)目中 git地址

2. package.json中添加命令

 // package.json
 "scripts": {
     ...
     "new-page": "node ./scripts/newPage.mjs"
 }

3. 執(zhí)行npm run new-page;

??提示"請輸入要生成的頁面"皮服,輸入規(guī)則是 a:b蕴侧,a表示頁面目錄裹纳,b表示頁面描述。輸入后回車,會在src/pages/下創(chuàng)建名為a的文件夾,并將/scripts/template/下的所有文件全部復(fù)制到a文件夾下恨狈。

??可以在template里添加自己項(xiàng)目的模板文件吗氏,比如router往产,store等等看自己項(xiàng)目的需求

4. newPage.mjs文件介紹

4.1 使用process.stdin獲取在控制臺中的輸入的內(nèi)容兴喂,即a:b蘑险;然后使用fs.mkdirSync創(chuàng)建文件夾,{recursive: true}表示允許創(chuàng)建多級目錄堆缘,例如:a/aa/aaa:b麻车;
```mjs
  // newPage.mjs  
  process.stdin.on('data', async (chunk) => {
    // 獲取輸入的信息
    const content = String(chunk).trim().toString();
    const inputSearch = content.search(':');
    if (inputSearch == -1) {
      errorLog('格式錯誤赁咙,請重新輸入');
      return;
    }
    // 拆分用戶輸入的名稱和描述
    inputName = content.split(':')[0];
    inputDesc = content.split(':')[1] || inputName;
    log(`將在 /src/pages 目錄下創(chuàng)建 ${inputName} 文件夾猿涨,并復(fù)制模板`);
    const targetPath = resolve('./src/pages', inputName);
    // 判斷同名文件夾是否存在
    const pageExists = fs.existsSync(targetPath);
    if (pageExists) {
      errorLog('頁面已經(jīng)存在俺附,請重新輸入');
      return;
    }
    // 創(chuàng)建目錄并復(fù)制文件
    fs.mkdirSync(targetPath, { recursive: true });
    successLog(`創(chuàng)建完成`);
    ...
    ...
  })
```
4.2 創(chuàng)建文件成功后,用fs.copyFileSync撮奏,遞歸復(fù)制各級文件
  // newPage.mjs
  ...
  ...
    const sourcePath = resolve('./scripts/template');
    copyFile(sourcePath, targetPath);
    successLog(`模板復(fù)制完成`);
  ...
  ...
  const copyFile = (sourcePath, targetPath) => {
    const sourceFile = fs.readdirSync(sourcePath, { withFileTypes: true });

    sourceFile.forEach((file) => {
      const newSourcePath = path.resolve(sourcePath, file.name);
      const newTargetPath = path.resolve(targetPath, file.name);
      //isDirectory() 判斷這個文件是否是文件夾青自,是就繼續(xù)遞歸復(fù)制其內(nèi)容
      if (file.isDirectory()) {
        isExist(newTargetPath);
        copyFile(newSourcePath, newTargetPath);
      } else {
        fs.copyFileSync(newSourcePath, newTargetPath);
      }
    });
  };
4.3 將文件復(fù)制完成后,重寫兩個文件淤井,pages.json肛炮、index.html。

??pages.json是用于記錄當(dāng)前已創(chuàng)建的所有文件名稱和描述汰具,一是為了構(gòu)建所有頁面時使用,二是為了去重验靡,防止新舊頁面名稱一樣導(dǎo)致原頁面被重置。

??index.html是項(xiàng)目啟動的根頁面亚情,包含本項(xiàng)目中所有的頁面列表黄伊,方便快速打開想要調(diào)試的頁面

  // newPage.mjs 
  /**
  * 重寫pages.json
  */
  async function setPagesFile(jsonData) {
    // 通過writeFile改變數(shù)據(jù)內(nèi)容
    log(`正在重寫pages.json文件`);
    prettier.resolveConfig(resolve('./', '.prettierrc.json'));
    const formatted = await prettier.format(JSON.stringify(jsonData), { parser: 'json' });
    fs.writeFile(path.resolve('./', 'pages.json'), formatted, 'utf-8', (err) => {
      if (err) throw err;
      successLog(`重寫完成`);
      setHtmlFile();
    });
  }

  /**
  * 重寫根目錄下的index.html勿锅,方便本地調(diào)試
  */
  async function setHtmlFile(pageObj) {
    log(`正在重寫根目錄下的index.html文件`, pageObj);
    // 先獲取html文件原內(nèi)容
    await fs.readFile(path.resolve('./', 'index.html'), 'utf-8', async (err, data) => {
      if (err) throw err;
      // 找到"<body>"位置,向其后插入用于跳轉(zhuǎn)的標(biāo)簽
      const bodyTagIndex = data.indexOf('<body>');
      if (bodyTagIndex === -1) {
        console.error('<body> 標(biāo)簽未找到');
        return;
      }
      // 在 <body> 后插入 <p> 標(biāo)簽
      const insertIndex = bodyTagIndex + '<body>'.length;
      const newContent = `${data.slice(0, insertIndex)}<p><a href="./src/pages/${inputName}/index.html">${inputDesc}</a></p>${data.slice(insertIndex)}`;
      // 將新得到的字符串格式化
      prettier.resolveConfig(resolve('./', '.prettierrc.json'));
      const formatted = await prettier.format(newContent, { parser: 'html' });
      fs.writeFile(path.resolve('./', 'index.html'), formatted, 'utf-8', (err) => {
        if (err) throw err;
        successLog(`重寫完成`);
        process.stdin.emit('end');
      });
    });
  }

四瞒大、實(shí)現(xiàn)需求2内列,單獨(dú)調(diào)試(dev)和打包(build)某個頁面

命令

npm run dev --page=a;
npm run build --page=a;

介紹 (主要修改vite.config.ts文件中的配置)

1. getBuildEnterPages()

根據(jù)命令中--page的值 動態(tài)配置build時的頁面入口嫩与,返回給 build.rollupOptions.input

  // vite.config.ts

  /**
  * 獲取build時的頁面入口
  * 該方法只支持單頁面的打包,不能支持全量打包,全量打包需要執(zhí)行build.mjs腳本
  */
  const getBuildEnterPages = () => {
    if (!npm_config_page && npm_lifecycle_event !== 'dev') {
      errorLog('請?jiān)诿钚泻笠?`--page=頁面目錄` 格式指定頁面目錄塞椎!');
      process.exit();
    }
    if (npm_lifecycle_event === 'build') {
      infoLog('正在打包');
    }
    // 打包指定頁面钱雷,遍歷pages.json骨坑,判斷頁面是否存在
    const filterArr = pages.filter(
      (item) => item.chunk.toLowerCase() == npm_config_page.toLowerCase()
    );
    if (!filterArr.length && npm_lifecycle_event !== 'dev') {
      errorLog('不存在此頁面礁遣,請檢查頁面目錄斑芜!');
      process.exit();
    }
    return {
      [npm_config_page]: resolve(__dirname, `src/pages/${npm_config_page}/index.html`)
    };
  };
  // defineConfig
  export default defineConfig({
    ...
    build: {
      rollupOptions: {
        input: getBuildEnterPages()// 指定打包頁面入口
      }
    }
    ...
  });

2. getEnterRoot()

動態(tài)修改root目錄

  // vite.config.ts

  /**
  * 動態(tài)修改項(xiàng)目根目錄入口
  * 1. 為了build后的文件結(jié)構(gòu)。不然index.html的目錄結(jié)構(gòu)太深了/dist/src/pages/a/index.html
  * 2. 修改root目錄可以實(shí)現(xiàn)dev單頁面還是dev全部頁面
  */
  const getEnterRoot = () => {
    // 如果是dev祟霍,且沒有指定--page則直接啟動所有頁面
    if (!npm_config_page && npm_lifecycle_event === 'dev') {
      return resolve(__dirname);
    }
    // 遍歷pages.json杏头,判斷頁面是否存在
    const filterArr = pages.filter(
      (item) => item.chunk.toLowerCase() == npm_config_page.toLowerCase()
    );
    if (!filterArr.length) {
      errorLog('不存在此頁面,請檢查頁面目錄浅碾!');
      errorLog('命令以 `--page=頁面目錄` 格式指定頁面目錄!');
      errorLog('若要打包全部頁面則需要執(zhí)行`npm run build-all`');
      process.exit();
    }
    return resolve(__dirname, `src/pages/${npm_config_page}`);
  };
  // defineConfig
  export default defineConfig({
    ...
    root: getEnterRoot(),
    ...
  });

3. 修改打包后的輸出路徑

  // vite.config.ts
  // defineConfig
  export default defineConfig({
    ...
    build: {
      outDir: resolve(__dirname, `dist/${npm_config_page}`), // 指定打包后的文件輸出路徑 npm_config_page即 --page的值
    }
    ...
  });

4. 修改環(huán)境變量路徑 envDir

  // vite.config.ts
  // defineConfig
  export default defineConfig({
    ...
    envDir: resolve(__dirname), // 由于修改了root地址大州,所以需要重新指回環(huán)境變量的路徑為根目錄
    ...
  });

五、實(shí)現(xiàn)需求3垂谢,同時調(diào)試(dev)和打包(build)所有頁面

命令

npm run dev;
npm run build-all;

1. npm run dev

dev所有頁面就是root配置成項(xiàng)目根目錄即可厦画,其實(shí)第四步里已經(jīng)實(shí)現(xiàn)了npm run dev啟動所有頁面了。

如果想嚴(yán)謹(jǐn)一點(diǎn),比如必須npm run dev-all根暑,可以改造一下getBuildEnterPagesgetEnterRoot()方法力试。

并在package.json中添加命令 "dev-all": "vite"

  // package.json
  "scripts": {
    ...
    "dev-all": "vite"
    ...
  }
const getBuildEnterPages = () => {
  if (npm_lifecycle_event === 'dev-all') {
    return {
      [npm_config_page]: resolve(__dirname)
    };
  }
  if (!npm_config_page && npm_lifecycle_event !== 'dev') {
    errorLog('請?jiān)诿钚泻笠?`--page=頁面目錄` 格式指定頁面目錄!');
    process.exit();
  }
  if (npm_lifecycle_event === 'build') {
    infoLog('正在打包');
  }
  // 打包指定頁面排嫌,遍歷pages.json畸裳,判斷頁面是否存在
  const filterArr = pages.filter(
    (item) => item.chunk.toLowerCase() == npm_config_page.toLowerCase()
  );
  if (!filterArr.length && npm_lifecycle_event !== 'dev') {
    errorLog('不存在此頁面,請檢查頁面目錄淳地!');
    process.exit();
  }
  return {
    [npm_config_page]: resolve(__dirname, `src/pages/${npm_config_page}/index.html`)
  };
};
const getEnterRoot = () => {
  // 如果是dev-all怖糊,則返回整個項(xiàng)目的根目錄
  if (!npm_config_page && npm_lifecycle_event === 'dev-all') {
    return resolve(__dirname);
  }
  // 遍歷pages.json,判斷頁面是否存在
  const filterArr = pages.filter(
    (item) => item.chunk.toLowerCase() == npm_config_page.toLowerCase()
  );
  if (!filterArr.length) {
    errorLog('不存在此頁面颇象,請檢查頁面目錄伍伤!');
    errorLog('命令以 `--page=頁面目錄` 格式指定頁面目錄!');
    errorLog('若要打包全部頁面則需要執(zhí)行`npm run build-all`');
    process.exit();
  }
  return resolve(__dirname, `src/pages/${npm_config_page}`);
};

2. npm run build-all

在package.json中添加命令"build-all": "node ./scripts/build.cjs",其實(shí)就是node執(zhí)行scripts文件夾中的build.mjs腳本

腳本邏輯很簡單遣钳,就是獲取pages.json中的所有頁面信息扰魂,然后根據(jù)記錄的信息生成npm run build --page=,for循環(huán)執(zhí)行所有命令蕴茴,打包構(gòu)建所有頁面劝评。

待優(yōu)化的點(diǎn):配置環(huán)境。如:npm run build-all test倦淀,npm run build-all development蒋畜,npm run build-all prerelease

  // package.json
  "scripts": {
    ...
    "build-all": "node ./scripts/build.cjs",
    ...
  }
// build.cjs
  const { exec } = require('child_process');
  const pagesArray = require('../pages.json');

  // 獲取命令行參數(shù)
  const args = process.argv;
  // 配置環(huán)境 比如npm run build-all:test npm run build-all:development  暫時沒啟用晃听,待優(yōu)化
  const commandLineArgs = args.slice(2);

  for (let i = 0; i < pagesArray.length; i++) {
    const page = pagesArray[i];
    // 定義要執(zhí)行的命令
    const commandToExecute = `npm run build${commandLineArgs[0] === 'test' ? ':test' : ''} --page=${page.chunk}`;
    exec(
      commandToExecute,

      (error, stdout, stderr) => {
        if (error) {
          console.error(`打包出錯: ${error.message}`);
          return;
        }

        console.log(`打包成功(${commandToExecute}):\n${stdout}`);
      }
    );
  }

其他

打包后會生成 tsconfig.tsbuildinfo vite.config.ts.timestamp-1730464365481-a0d864a250d37.mjs文件百侧,這兩個文件都可以隨時刪除,且記錄在.gitignore忽略文件中

tsconfig.tsbuildinfo 用于記錄構(gòu)建的文件信息能扒,以便下次構(gòu)建是跳過未發(fā)生變化的文件佣渴,提高構(gòu)建速度

vite.config.ts.timestamp-1730464365481-a0d864a250d37.mjs 當(dāng)build構(gòu)建失敗報錯時會生成該文件,用于記錄構(gòu)建信息的初斑,方便定位失敗原因

參考文檔

?? https://juejin.cn/post/7223286759630127159#heading-23;

?? 這篇文章寫的很詳細(xì)辛润,但是吐槽一下:內(nèi)容太多了,除了多頁面項(xiàng)目的搭建见秤,還加了一些其他方面的東西砂竖,有點(diǎn)冗余。其實(shí)可以分多篇文章介紹的鹃答。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乎澄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子测摔,更是在濱河造成了極大的恐慌置济,老刑警劉巖解恰,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浙于,居然都是意外死亡护盈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門羞酗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腐宋,“玉大人,你說我怎么就攤上這事檀轨⌒鼐海” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵参萄,是天一觀的道長撤师。 經(jīng)常有香客問我,道長拧揽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任腺占,我火速辦了婚禮淤袜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衰伯。我一直安慰自己铡羡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布意鲸。 她就那樣靜靜地躺著烦周,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怎顾。 梳的紋絲不亂的頭發(fā)上读慎,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音槐雾,去河邊找鬼夭委。 笑死,一個胖子當(dāng)著我的面吹牛募强,可吹牛的內(nèi)容都是我干的株灸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼擎值,長吁一口氣:“原來是場噩夢啊……” “哼慌烧!你這毒婦竟也來了顷歌?” 一聲冷哼從身側(cè)響起藕帜,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坎穿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后轮锥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡塔粒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年阀溶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玄括。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡冯丙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出遭京,到底是詐尸還是另有隱情胃惜,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布哪雕,位于F島的核電站船殉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斯嚎。R本人自食惡果不足惜利虫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望堡僻。 院中可真熱鬧糠惫,春花似錦、人聲如沸钉疫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牲阁。三九已至固阁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間城菊,已是汗流浹背备燃。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留役电,地道東北人赚爵。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像法瑟,于是被迫代替她去往敵國和親冀膝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355