CEF, ES6, Angular 2, WebPack 2 .Net Core десктопное приложение без серверной части

    Это продолжение статей:

    CEF, ES6, Angular 2, TypeScript использование классов .Net Core. Создание кроссплатформенного GUI для .Net с помощью CEF
    CEF, Angular 2 использование событий классов .Net Core

    Основная идея этих статей — создание кроссплатформенных приложений на CEF с использованием Angular 2 и .Net Core. Чтобы отвязаться от сервера, используем свежий WebPack и настроим на локальное использование файлов.

    Для начала создадим package.json.

    Содержимое package.json
    {
      "name": "helloapp",
      "version": "1.0.0",
      "scripts": {
        "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
        "lite": "lite-server",
        "tsc": "tsc",
        "tsc:w": "tsc -w"
      },
      "dependencies": {
        "@angular/common": "~2.4.0",
        "@angular/compiler": "~2.4.0",
        "@angular/core": "~2.4.0",
        "@angular/forms": "~2.4.0",
        "@angular/http": "~2.4.0",
        "@angular/platform-browser": "~2.4.0",
        "@angular/platform-browser-dynamic": "~2.4.0",
        "@angular/router": "~3.4.0",
        "@angular/upgrade": "~2.4.0",
        "angular-in-memory-web-api": "~0.2.4",
        "babel-preset-es2015": "^6.22.0",
        "babel-preset-es2016": "^6.22.0",
        "babel-preset-react": "^6.23.0",
        "bootstrap": "^3.3.7",
        "core-js": "^2.4.1",
        "css-loader": "^0.26.1",
        "jquery": "^3.1.1",
        "postcss-loader": "^1.3.0",
        "raw-loader": "^0.5.1",
        "reflect-metadata": "^0.1.9",
        "rxjs": "^5.1.1",
        "sass-loader": "^6.0.0",
        "style-loader": "^0.13.1",
        "systemjs": "^0.20.7",
        "to-string-loader": "^1.1.5",
        "ts-loader": "^2.0.0",
        "typescript": "^2.1.6",
        "webpack": "^2.2.0",
        "webpack-fail-plugin": "^1.0.5",
        "webpack-notifier": "^1.5.0",
        "zone.js": "^0.7.7"
      },
      "devDependencies": {
        "@types/core-js": "^0.9.35",
        "@types/node": "^6.0.46",
        "babel-core": "6.23.1",
        "babel-eslint": "7.1.1",
        "babel-loader": "6.3.0",
        "babel-plugin-__coverage__": "^11.0.0",
        "babel-polyfill": "^6.0.0",
        "babel-preset-angular2": "^0.0.2",
        "babel-preset-es2015": "^6.22.0",
        "bootstrap-loader": "^2.0.0-beta.20",
        "bootstrap-sass": "^3.3.7",
        "chunk-manifest-webpack-plugin": "^1.0.0",
        "concurrently": "^3.1.0",
        "css-loader": "^0.26.1",
        "css-to-string-loader": "^0.1.2",
        "extract-text-webpack-plugin": "^2.0.0-rc.3",
        "file-loader": "^0.10.0",
        "html-webpack-plugin": "^2.28.0",
        "jquery": "^3.1.1",
        "lite-server": "^2.2.2",
        "node-sass": "^4.5.0",
        "resolve-url-loader": "^1.6.1",
        "sass-loader": "^6.0.0",
        "style-loader": "^0.13.1",
        "typescript": "^2.1.5",
        "url-loader": "^0.5.7"
      }
    } 


    Здесь собраны загрузчики, ссылки на необходимые файлы итд, которые были подобраны долгими исканиями.

    Так же необходим tsconfig.json для компиляции TypeScript-файлов.

    Содержимое tsconfig.json
    {
      "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": true,
        "suppressImplicitAnyIndexErrors": true,
        "lib": [ "es6", "dom" ],
        "types": [ "node" ],
        "typeRoots": [
          "node_modules/@types"
        ]
      },
      "exclude": [
        "node_modules",
        "wwwroot",
        "**/*.spec.ts"
      ],
    
      "compileOnSave": true
    }


    Теперь мы можем создать на основании package.json директорию node_modules с помощью команды npm install из директории приложения.

    Прежде чем вызывать WebPack, нужно создать несколько файлов polyfills.ts.

    Содержимое polyfills.ts
    import 'core-js/es6';
    import 'core-js/es7/reflect';
    require('zone.js/dist/zone');
    
    if (process.env.ENV === 'production') {
      // Production
    } else {
      // Development and test
      Error['stackTraceLimit'] = Infinity;
      require('zone.js/dist/long-stack-trace-zone');
    }


    Добавить в main.ts ссылки на Bootstap и JQuery:

    
    import 'jquery';
    import 'bootstrap-loader';
    import 'bootstrap/dist/css/bootstrap.css';
    import 'bootstrap/dist/css/bootstrap-theme.css';

    Теперь нам доступны стили, glyphicons и тд. Теперь перейдем к самому главному, а именно webpack.config.js, на основании которого и будут собираться нужные нам файлы.

    var webpack = require('webpack');
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var ExtractTextPlugin = require("extract-text-webpack-plugin");
    
    var helpers = require('./helpers');
    var babelOptions = {
      "presets": [
        "react",
        [
          "es2015",
          {
            "modules": false
          }
        ],
        "es2016"
      ]
    };
    
    module.exports = {
      cache: true,
      entry: {
        polyfills: './app/polyfills.ts',
        main: './app/main.ts',
        vendor: [
          'babel-polyfill'
        ]
      },
      output: {
        path: './wwwroot',
        filename: './scripts/[name].js',
        chunkFilename: './scripts/[chunkhash].js',
        publicPath: './'
      },
      module: {
        rules: [{
          test: /\.ts(x?)$/,
          exclude: /node_modules/,
          include: /app/,
          use: [
            {
              loader: 'babel-loader',
              options: babelOptions
             
            },
            {
              loader: 'ts-loader'
            }
          ]
        }, {
          test: /\.js$/,
          exclude: /node_modules/,
          include: /app/,
          use: [
            {
              loader: 'babel-loader',
              options: babelOptions
            }
          ]
        },
          { test: /\.html$/, loader: 'raw-loader', exclude: [helpers.root('app/index.html')] },
          { test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
          { test: /\.css$/, loader: 'css-to-string-loader!css-loader' },
          { test: /\.scss$/, loaders: ['style', 'css', 'postcss', 'sass'] },
          { test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url-loader?limit=10000' },
          { test: /bootstrap\/dist\/js\/umd\//, loader: 'imports?jQuery=jquery' }
    
    
        ]
      },
       resolve: {
           extensions: ['.ts', '.tsx', '.js','.css']
       },
       plugins: [
      // Workaround for angular/angular#11580
      
      new webpack.optimize.CommonsChunkPlugin({
          name: ['app', 'vendor', 'polyfills']
      }),
    
      new HtmlWebpackPlugin({
          template: 'app/index.html'
    
      }),
        new ExtractTextPlugin(
            {
                filename: 'styles.css',
                disable: false,
                allChunks: true
            }
    ),
    new webpack.ProvidePlugin({
        jQuery: 'jquery',
        $: 'jquery',
        jquery: 'jquery'
    })
       ]
    };
    

    Здесь собрано всё, чтобы использовать es6 и всё собиралось в несколько файлов в директории
    './wwwroot'. Теперь можно запустить webpack --config webpack.config.js, который и создаст нужные нам файлы.

    Но нам придется подправить выходной index.html. Почему-то для:

    <script type="text/javascript" src="././scripts/polyfills.js"></script>
    <script type="text/javascript" src="././scripts/vendor.js">
    </script><script type="text/javascript" src="././scripts/main.js"></script>

    Добавляются лишние ./ — убрав ./, мы можем запустить index.html в любом браузере. Но нас интересует наш cefsimple.exe:

    Укажем в адресе файла путь к index.html, например, d:\CEF\CefProgects\TestTypeScript\TestTypeScript\wwwroot\index.html

    И Вуаля, мы работаем автономно, без Web-сервера. Если нужно подключиться к серверу, у нас есть все на .Net. Например, .Net Core, WCF и ODATA-клиенты.

    Нужно добавить что я отказался от Routing в пользу NgSwitch

    Так как в проект построен на использовании systemjs.config.js, то в нем нет поддержки require
    который необходим для WebPack, что бы весь Html код интегрировался в js код.

    Так как не нашел в TS условную компиляцию, для отладки необходимо закомментировать require

    @Component({
        selector: 'nav-menu',
        template:require('./navmenu.component.html'),
        styles: [require('./navmenu.component.css')]
    
     //   templateUrl: 'app/navmenu/navmenu.component.html',
     //   styleUrls: ['app/navmenu/navmenu.component.css']
    })
    


    И раскомментировать

    
     //   templateUrl: 'app/navmenu/navmenu.component.html',
     //   styleUrls: ['app/navmenu/navmenu.component.css']
    


    исходники и программы и инструкцию использования можно найти в предыдущих статьях в самом низу.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 14

      +1

      Спасибо за работу. Жалко что мало плюсов набирают статьи. Но я внимательно слежу за вашими статьями и читаю с большим удовольствием. Пожалуйста, не бросайте.

        0
        Спасибо!
        0
        Добавляются лишние ./

        У вас в publicPath стоит "./" поэтому и добавляется.
          0
          Это понятною Но для файлов ./ это текущая директория. Если её не указать, то необходимые файлы ищутся с корня диска
            0
            Я бы вам рекомендовал поглядеть документацию еще раз. Тык.
              0
              Только для локальных файлов это не работает. Начинает искать с корня диска. А задача была использовать локальные файлы без серверной части
          0
          Статья по теме dotnet new angular and dotnet new react
          0
          Сергей из исходников не очень понятно как запускать на отличных от windows платформах.
          0
          Для этого нужно перекомпилировать под каждую платформу SefSimple
          Плюс я не пробовал на Linux, то возможно надо будет изменить загрузку .Net Core
          Вот моя первая статья Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux

          На просторах интернета было найдено решение: Hosting .NET Core Clr in your own process и simpleCoreCLRHost и
          initializeCoreCLR createDelegate и
          unixinterface

          Суть подключения заключается в загрузке библиотеки coreclr.dll, получения нужных интерфейсов и запуск CLR Runtime Host.
            0
            Ясно, спасибо, наверное стоит упомянуть в статье, что пример чисто теоритически кросс-платформен потому что построен на кросс-плафтформенных технологиях, но сама интеграция CEF и .NET Core у вас Windows only. Но в любом случае спасибо за статьи и ссылки, я недавно проектировал что-то похожее и остановился на electron для фронтэнда + .NET Core для интеграции с железом через DllImport, но вся коммуникация была IPC через WebSocket, т.е. приложение состоит из двух процессов. Так же рассматривали edge(для electron), но все же out of process в нашем случае лучше подошёл так как коммуникация простая(по типу событий) и вызовов немного + битность и стабильность процесса electron не страдала если приходится загружать нестабильные нативные DLL для различных железок от вендоров этих железок, так как это делает другой(.NET Core) процесс.
              0
              Спасибо! Суть то в том, что ты первый кто заинтересовался моими разработками.
              Я один. При этом 1С ник, который решил интегрировать .Net в 1С. Ноги растут оттуда.
              Я мог бы сделать и реальную кроссплатформенность, но главное показать как это работает.
              Можно добавить использование JS объектов и функций на стороне .Net.

              Что касается out of process каждый решает сам. Например по опыты в 1С приходится сочетать свои сборки и например тот же AngleSharp проще использовать на стороне 1С итд.
              Можно использовать динамическую компиляцию итд.
              Использовать out of process несколько сложнее.
                0
                Изначально я хотел делать аналог RPC. Его несложно сделать и на существующей модели. По аналогии с .Net Core, обмен с 1C по TCP/IP между различными устройствами

                По сути внешне все остается тем же

            Only users with full accounts can post comments. Log in, please.